Any way to declare happen-before relatioship in Spring Boot? - java

I have some validation code that should run on server startup and make sure various conditions are met so whoever deploys the server don't messes up the DB or start the server with bad security configurations etc. To do that I created a bean
#Component
public class ApplicationStartupConditionsValidationBean {
static class ServerInitializationError extends Error{
public ServerInitializationError(String msg){
super(msg);
}
}
private Environment springEnv;
private String datasourceURL;
private String ddlAuto;
private static final Logger logger = LogManager.getLogger();
#Autowired
public ApplicationStartupConditionsValidationBean(Environment springEnv) throws Exception {
this.springEnv = springEnv;
this.datasourceURL = springEnv.getProperty("spring.datasource.url");
this.ddlAuto = springEnv.getProperty("spring.jpa.hibernate.ddl-auto");
validateStartupConditions();
}
public boolean isDBLocal(){
return datasourceURL.startsWith("jdbc:postgresql://localhost:");
}
private String disallowedParamMsg(String optionName, String optionValue){
return "option " + optionName + "=" + optionValue + " not allowed in production";
}
private void reject(String msg) throws ServerInitializationError{
String rejectionMsg = "startup conditions validation failed with msg: " + msg;
logger.error(rejectionMsg);
throw new ServerInitializationError(rejectionMsg);
}
private void reject(String paramName, String paramValue) throws ServerInitializationError{
reject(disallowedParamMsg(paramName, paramValue));
}
private void validateDatasourceParams(){
if(!isDBLocal() &&
!ddlAuto.equals("validate")){
reject("ddl-auto", ddlAuto);
}
}
public void validateStartupConditions() throws Exception{
logger.info("validating startup conditions");
validateDatasourceParams();
// more validation logic...
logger.info("startup conditions validation succeeded, proceeding with boot");
}
}
The way I would have wanted to use this class is to define what beans this must come before. In the example here I would have wanted to make sure this bean would be created before the DataSource bean is created, so that "ddl-auto=create" doesn't slip in production. I Know about the #DependsOn annotation but I would have wanted to do the reverse, and declare that this bean #HappensBefore a list of other beans. Is there any way to do this?
Thanks!

To run code before "normal" beans get created, you can use a BeanFactoryPostProcessor.
To declaratively add additional dependencies among beans, you could also use a BeanPostProcessor, but that sounds needlessly cumbersome for your use case.

Related

Java - Getter/Setter, behavior and Interfaces

I have a question, a little bit theoretical:
Assume, I have the following classes :
interface ReportInterface {
void execute();
}
class Report implements ReportInterface {
private final Repository rep;
Report(Repository ref){
this.rep = ref;
}
public void execute(){
//do some logic
}
}
class ReportWithSetter implements ReportInterface {
private final Repository rep;
private String release;
ReportWithSetter(Repository ref){
rep = ref;
}
public void execute(){
if (release == null) throw IlligalArgumentException("release is not specified");
//do some logic
}
public void setRelease(String release){
this.release=release;
}
}
The second report needs an additional parameter release to work properly, but my interface is defined without parameters for execute method, so I work around it with a setter method, so it would look like:
ReportWithSetter rep2 = new ReportWithSetter (rep);
rep.setRelease("R1.1");
rep.execute();
So I don't like this additional rep.setRelease. I looks weird and artificial - a user of this class may be confused, and for example, if I make the class as a singleton bean in Spring, it is a source of potential error, if it is requested for the second time and somebody forgets to trigger rep.setRelease for the second time. Besides putting it into constructor (I want to make it a spring bean), what would be the best practice to handling this situation?
Assuming you are allowed to change the interface, here are a few solutions I can think of:
Solution #1
void execute(Optional<String> release);
or
void execute(#Nullable String release);
and then use them for Report class as execute(Optional.empty()) or execute(null).
Solution #2
void execute(String... release);
and then use it for Report class as execute() and for ReportWithSetter class as execute("R1.1").
Solution #3
Define both void execute(); and void execute(String release); in the interface. Then while implementing, throw UnsupportedOperationException in the method you don't need. For example, in Report class, you would do:
public void execute(){
//do some logic
}
public void execute(String release){
throw new UnsupportedOperationException("Use the overloaded method");
}
You can also make both these methods as default in the interface, so your implementation classes don't have to worry about implementing the unsupported method.
Use whichever is most readable and maintainable for you.
Solution 1: Spring Dependency Injection - Field Injection:
Spring's Dependency Injection works with reflection, so Setter methods are not required.
So if you make your Report class a Spring Bean and use #Autowired to inject another bean, then the Setter method is not required.
It would look like this:
#Component
class ReportWithRelease implements ReportInterface {
#Autowired private final Repository rep;
#Autowired private Release release;
public void execute(){
if (release == null) throw IlligalArgumentException("release is not specified");
//do some logic
}
}
I changed "String release" to "Release release", because making a bean of "String" would be also strange. So the "Release" class would have to contain your "String release".
If "String release" contains only some configured value, which does not change at runtime. Then you can use #Value to read its String value from a properties file.
Solution 2: Spring Constructor Injection:
Constructor injection is another option, which is even more recommended.
Then your Report bean would look like this:
#Component
class ReportWithRelease implements ReportInterface {
private Repository rep;
private Release release;
#Autowired
public ReportWithRelease(Repository rep, Release release) {
this.rep = rep;
this.release = release;
}
public void execute(){
if (release == null) throw IlligalArgumentException("release is not specified");
//do some logic
}
}
Factory method patterns are good if you want to create instances of different classes of same interface.
class MyFactory {
ReportInterface createInstance(Class clazz, String... args) {
if (Report.class.equals(clazz)) {
return new Report();
}
if (ReportWithSetter.class.equals(clazz)) {
return new ReportWithSetter(args[0]);
}
throw new IllegalArgumentException(clazz.getName());
}
}
Spring of course offers autowiring, but introducing #AutoWire should be done for systematic purposes.
Here you can do with a two-stage execute, a factory.
class ReportFactory /*ReportWithSetter*/ {
private final Repository rep;
private final String release;
private final ReportInterface report = ...;
ReportFactory (Repository rep, String release) {
this.rep = rep;
this.release = release;
}
public ReportInterface report() {
return report;
}
}
new ReportFactory(rep, release).execute();

Springboot autowired null value

I'm fairly new to Spring & Spring boot, trying to wrap my head around with the concepts.
I have this sample class, this is just a typed up code to show what i'm trying to do. There are no compilation errors. When I start the server, the MQConnection class code gets executed, the mq properties from the appplication.properties are read and printing. But when another class tries to call the send message to MQ, i'm seeing NUllPointerException
#Component
public class MQConnection {
private String username;
private String password;
private String host;
private Connection connection;
#Autowired
public MQConnection(#value("${username}") String username,
#value("${password}") String password, #value("${host}") String host) {
this.username = username;
this.password = password;
this.host = host;
init();
}
public getConnection() {
return connection
}
private init() {
connection = mqconnection;
System.out.println(username, password, host); // I see the value when the server is started
//code to connect to mq
}
}
What am I missing, these autowired & beans is really confusing for me as i'm new to Spring world. Am I using right flow or completely absurd, I don't know
#Component
public class MQSendMessage {
#Autowired
MQConnection mqConnection;
public void sendMessage(String message) {
connection = mqConnection.getConnection(); //NULL value
//send messageCode throws nullpointerexception
}
}
public class AnotherClass {
#Autowired
MQSendMessage messageq;
public doSomething() {
messageq.sendMessage("hello world");
}
}
Any help to fix the connection that throws nullpointer
It looks like AnotherClass is not instantiated by Spring container. If you want to use Spring-annotation like convention then you have to annotate your class with e.g.#Component annotation. Otherwise Spring wont instantiate this object for you.
Useful tip
Try using constructor injection instead of a field injection. Just like in your MQConnection class. You can even mark all your class fields instantiated in the construct with final keyword so you will be sure that these values wont change (if they are immutable of course) during bean life cycle. Then AnotherClass could look like this:
public class AnotherClass {
private final MQSendMessage messageq;
#Autowired
public AnotherClass(MQSendMessage messageq) {
this.messageq = messageq
}
public doSomething() {
messageq.sendMessage("hello world");
}
}
Spring Boot documentation
Also please read carefully Spring Boot documentation on Spring Beans and dependency injection. It is very well written and describes basic concepts in details. It will make your learning much easier and faster.
I hope it helps.

MyBatis #Mapper declaration

Spring Boot | MyBatis
When I try to declare a mybatis mapper in controller, it gets underlined by IDE, and doesn't compile.
#Controller
#RequestMapping("demo")
#MapperScan("com.sample.mapper")
public class MessageController {
private static final String MESSAGE = "message";
private static final String INDEX = "index";
#Autowired
private MessageMapper messageMapper;
#RequestMapping("printMessage/{message}")
public String printMessage(ModelMap modelMap) {
modelMap.addAttribute(MESSAGE, "M");
return INDEX;
}
#RequestMapping("printHello")
public String printHello(ModelMap modelMap) {
modelMap.addAttribute(MESSAGE, "Hello, ");
return INDEX;
}
I got this class compiled somehow recently, however, when I try to use messageMapper instance, like messageMapper.insert() as it's not assigned any value, it gives me NullPointerException. It seems like Spring is for some reason is not working for me.
According to the documencation, I think the #MapperScan is not the right class, they cannot be autowired because they are not in the context on controller creation time. When it is defined in Mybatis XML config file, it is loaded with an Sql Session Factory Provider, a place that actually makes more sense, then it shall not be different with annotations style.

DeltaSpike custom ConfigSource with CDI

I am trying to define a custom DeltaSpike ConfigSource. The custom config source will have the highest priority and check the database for the config parameter.
I have a ConfigParameter entity, that simply has a key and a value.
#Entity
#Cacheable
public class ConfigParameter ... {
private String key;
private String value;
}
I have a #Dependent DAO that finds all config parameters.
What I am trying to do now, is define a custom ConfigSource, that is able to get the config parameter from the database. Therefore, I want to inject my DAO in the ConfigSource. So basically something like
#ApplicationScoped
public class DatabaseConfigSource implements ConfigSource {
#Inject
private ConfigParameterDao configParameterDao;
....
}
However, when registering the ConfigSource via META-INF/services/org.apache.deltaspike.core.spi.config.ConfigSource, the class will be instantiated and CDI will not work.
Is there any way to get CDI working in this case?
Thanks in advance, if you need any further information, please let me know.
The main problem is, that the ConfigSource gets instantiated very early on when the BeanManager is not available yet. Even the JNDI lookup does not work at that point in time. Thus, I need to delay the injection/lookup.
What I did now, is add a static boolean to my config source, that I set manually. We have a InitializerService that makes sure that the system is setup properly. At the end of the initialization process, I call allowInitialization() in order to tell the config source, that the bean is injectable now. Next time the ConfigSource is asked, it will be able to inject the bean using BeanProvider.injectFields.
public class DatabaseConfigSource implements ConfigSource {
private static boolean allowInit;
#Inject
private ConfigParameterProvider configParameterProvider;
#Override
public int getOrdinal() {
return 500;
}
#Override
public String getPropertyValue(String key) {
initIfNecessary();
if (configParameterProvider == null) {
return null;
}
return configParameterProvider.getProperty(key);
}
public static void allowInitialization() {
allowInit = true;
}
private void initIfNecessary() {
if (allowInit) {
BeanProvider.injectFields(this);
}
}
}
I have a request-scoped bean that holds all my config variables for type-safe access.
#RequestScoped
public class Configuration {
#Inject
#ConfigProperty(name = "myProperty")
private String myProperty;
#Inject
#ConfigProperty(name = "myProperty2")
private String myProperty2;
....
}
When injecting the Configuration class in a different bean, each ConfigProperty will be resolved. Since my custom DatabaseConfigSource has the highest ordinal (500), it will be used for property resolution first. If the property is not found, it will delegate the resolution to the next ConfigSource.
For each ConfigProperty the getPropertyValue function from the DatabaseConfigSource is called. Since I do not want to retreive the parameters from the database for each config property, I moved the config property resolution to a request-scoped bean.
#RequestScoped
public class ConfigParameterProvider {
#Inject
private ConfigParameterDao configParameterDao;
private Map<String, String> configParameters = new HashMap<>();
#PostConstruct
public void init() {
List<ConfigParameter> configParams = configParameterDao.findAll();
configParameters = configParams.stream()
.collect(toMap(ConfigParameter::getId, ConfigParameter::getValue));
}
public String getProperty(String key) {
return configParameters.get(key);
}
}
I could sure change the request-scoped ConfigParameterProvider to ApplicationScoped. However, we have a multi-tenant setup and the parameters need to be resolved per request.
As you can see, this is a bit hacky, because we need to explicitly tell the ConfigSource, when it is allowed to be instantiated properly (inject the bean).
I would prefer a standarized solution from DeltaSpike for using CDI in a ConfigSource. If you have any idea on how to properly realise this, please let me know.
Even though this post has been answered already I'd like to suggest another possible solution for this problem.
I managed to load properties from my db service by creating an #Signleton #Startup EJB which extends the org.apache.deltaspike.core.impl.config.BaseConfigSource and injects my DAO as delegate which I then registered into the org.apache.deltaspike.core.api.config.ConfigResolver.
#Startup
#Singleton
public class DatabaseConfigSourceBean extends BaseConfigSource {
private static final Logger logger = LoggerFactory.getLogger(DatabaseConfigSourceBean.class);
private #Inject PropertyService delegateService;
#PostConstruct
public void onStartup() {
ConfigResolver.addConfigSources(Collections.singletonList(this));
logger.info("Registered the DatabaseConfigSourceBean in the ConfigSourceProvider ...");
}
#Override
public Map<String, String> getProperties() {
return delegateService.getProperties();
}
#Override
public String getPropertyValue(String key) {
return delegateService.getPropertyValue(key);
}
#Override
public String getConfigName() {
return DatabaseConfigSourceBean.class.getSimpleName();
}
#Override
public boolean isScannable() {
return true;
}
}
I know that creating an EJB for this purpose basically produces a way too big overhead, but I think it's a bit of a cleaner solution instead of handling this problem by some marker booleans with static accessors ...
DS is using the java se spi mechanism for this which is not CD'Injectable'. One solution would be to use the BeanProvider to get hold of your DatabaseConfigSource and delegate operations to it.

Expression in annotation using fields of class in Spring Security

I use method level security. In class I annotated some methods, expressions use fields of this class. But I see SpEL exceptions, that I can't reference them.
Here is part of code of this class. In expressions I want to use field repPrefix, but I receive exceptions that it's an unknown variable.
#Component("c2rTableManager")
#Scope("prototype")
public class C2RTableManager implements TableManager {
private final TableManager tableManager;
private final String repPrefix;
#Autowired
private SecurityInfoService securityInfoService;
public C2RTableManager(TableManager tableManager, String repository) {
this.tableManager = tableManager;
this.repPrefix = repository + "__";
}
...some methods
#Override
#PreAuthorize("hasRole('DBA') || hasPermission(repPrefix + #table, 'TABLE', 'DELETE_TABLE')")
public void dropTable(String table) throws InterruptedException, IOException {
tableManager.dropTable(table);
}
...other methods
}
If I write another way, expressions AREN'T EVALUATED at all. Can't understand why.
#Component("c2rTableManager")
#Scope("prototype")
public class C2RTableManager implements TableManager {
private final TableManager tableManager;
private final String repPrefix;
#Autowired
private SecurityInfoService securityInfoService;
public C2RTableManager(TableManager tableManager, String repository) {
this.tableManager = tableManager;
this.repPrefix = repository + "__";
}
...some methods
#Override
public void dropTable(String table) throws InterruptedException, IOException {
dropTable(table, repPrefix);
}
#PreAuthorize("hasRole('DBA') || hasPermission(#repPrefix + #table, 'TABLE', 'DELETE_TABLE')")
public void dropTable(String table, String repPrefix) throws InterruptedException, IOException {
tableManager.dropTable(table);
}
...other methods
}
How can I write expressions for methods of class using values of fields of this class?
I do not have enough reputation for adding a comment.
From the Spring Security docs available at http://docs.spring.io/spring-security/site/docs/3.0.x/reference/el-access.html
Here we're actually using a method argument as part of the expression
to decide whether the current user has the “admin”permission for the
given contact. The built-in hasPermission() expression is linked into
the Spring Security ACL module through the application context, as
we'll see below. You can access any of the method arguments by name as
expression variables, provided your code has debug information
compiled in.
Please stress on the Last sentence. Check the below two points:
Did you Compile the classes with debug flag on?
Did you enable the method level security with this declaration:<global-method-security pre-post-annotations="enabled"/>
I needed to declare bield as public
private final String repPrefix;
And write annotation with link to this
#PreAuthorize("hasRole('DBA') || hasPermission(repPrefix + #table, 'TABLE', 'DELETE_TABLE')")

Categories