I am trying to set up a clamav virus scanner in a Spring Boot environment. So I want to set the host and port in a properties file, clamav.properties, located in my resources directory along with the application.properties file. it looks like this:
clamav.host=localhost
clamav.port=3310
clamav.timeout=1000
I have this class:
#ConfigurationProperties("clamav.properties")
public class ClamAvClient {
static final Logger logger = LoggerFactory.getLogger(ClamAvClient.class);
#Value("${clamav.host}")
private String clamHost;
#Value("${clamav.port}")
private int clamPort;
#Value("${clamav.timeout}")
private int clamTimeout;
public boolean ping() throws IOException {
logger.debug("Host:"+clamHost+" Port:"+clamPort);
blah.....
}
private static byte[] asBytes(String s) {
return s.getBytes(StandardCharsets.US_ASCII);
}
public String getClamHost() {
return clamHost;
}
public void setClamHost(String clamHost) {
this.clamHost = clamHost;
}
public int getClamPort() {
return clamPort;
}
public void setClamPort(int clamPort) {
this.clamPort = clamPort;
}
public int getClamTimeout() {
return clamTimeout;
}
public void setClamTimeout(int clamTimeout) {
this.clamTimeout = clamTimeout;
}
}
It's not connecting and in the logs I get this:
2017-09-23 20:39:45.947 DEBUG 28857 --- [http-nio-8080-exec-2] xxx.ClamAvClient : Host:null Port:0
So those values are clearly not getting set. What am I doing wrong? I am using the managed version of spring-boot-starter-web, which my Eclipse is saying is 1.4.3-RELEASE
Any Ideas?
Either use #ConfigurationProperties to map group of properties to a Class using Configuration Processor.
Using #Value inside #ConfigurationProperties doesn`t look right.
All you need to map your properties to the class is :
#Configuration
#ConfigurationProperties(prefix="clamav")
public class ClamAvClient {
static final Logger logger = LoggerFactory.getLogger(ClamAvClient.class);
private String host;
private int port;
private int timeout;
//getters and setters
}
prefix ="clamav" matches your prefixes in the properties file.
host,port,timeout matches the properties of the class.
Related
In a service file I would simply use #Value and initialize the variable instially there. I have tried this approach in a model class but (I assume how things get autowired and that its a model class) this results in it always being null.
The need for this comes out that in different environments the default value is always different.
#Value("${type}")
private String type;
I would avoid trying to use Spring logic inside the models as they are not Spring beans themselves. Maybe use some form of a creational (pattern) bean in which the models are constructed, for example:
#Component
public class ModelFactory {
#Value("${some.value}")
private String someValue;
public SomeModel createNewInstance(Class<SomeModel> clazz) {
return new SomeModel(someValue);
}
}
public class SomeModel {
private String someValue;
public SomeModel(String someValue) {
this.someValue = someValue;
}
public String getSomeValue() {
return someValue;
}
}
#ExtendWith({SpringExtension.class})
#TestPropertySource(properties = "some.value=" + ModelFactoryTest.TEST_VALUE)
#Import(ModelFactory.class)
class ModelFactoryTest {
protected static final String TEST_VALUE = "testValue";
#Autowired
private ModelFactory modelFactory;
#Test
public void test() {
SomeModel someModel = modelFactory.createNewInstance(SomeModel.class);
Assertions.assertEquals(TEST_VALUE, someModel.getSomeValue());
}
}
I have a Spring Boot application with the below application.properties:
message=default1
security.url=defaultSecurityURL.com
security.authMethod=defaultAuth
security.roles=USER,ADMIN,PARTNER
message_temp=dummyvaluehere
Now, I have a Java class that reads all these variables from the application.properties:
#ConfigurationProperties
#ConstructorBinding
public final class EnvConfig {
private final String message;
private final Security security; //there is a Security class defined seperately
private final String messageTemp;
public EnvConfig(String message, Security security, String messageTemp) {
super();
this.message = message;
this.security = new Security(security.getUrl(), security.getAuthMethod(), security.getRoles()); //this is done for immutability
this.messageTemp = messageTemp;
}
public String getMessage() {
return message;
}
public Security getSecurity() {
return security;
}
public String getMessageTemp() {
return messageTemp;
}
#Override
public String toString() {
return "EnvConfig [message=" + message + ", security=" + security + ", messageTemp="
+ messageTemp + "]";
}
}
Everything works fine. Now I want to add 2 more variables to my application.properties file:
message.default.app.deploy.strategy=bg
message.default.app.deploy.retries=3
But I don't want to read these variables by declaring a new class (like I created a Security class above). How can I read these values in my EnvConfig class?
Look in my answer of this SO question which offers a generic way to access all variables of the spring environment (by its string key).
I am using log4j2. And i have deffined custom appender and pattern for it and setting in up programatically:
public static void initCustomLogging(List<Appender> appenders) {
LoggerContext loggerContext = (LoggerContext) LogManager.getContext(false);
Configuration configuration = loggerContext.getConfiguration();
LoggerConfig rootLoggerConfig = configuration.getLoggerConfig("");
for (Appender appender : appenders) {
appender.start();
rootLoggerConfig.addAppender(appender, Level.INFO, null);
}
loggerContext.updateLoggers();
}
Where i create my appender like:
private static final String CUSTOM_PATTERN = "{\"time\":\"%d{HH:mm:ss.SSS}\",\"data\":%msg}";
private static final Layout CUSTOM_LAYOUT = PatternLayout.newBuilder()
.withPattern(CUSTOM_PATTERN)
.build();
Appender appender = CustomAppender.createAppender("custom",CUSTOM_LAYOUT)
However i want to add custom arguments to my pattern, for example [%requestInfo] where this would call some static method. I know i can define my converter like:
#Plugin(name = "MyConverter", category = "Converter")
#ConverterKeys({"requestData"})
public class MyConverter extends LogEventPatternConverter {
private MyConverter (final String name, final String style) {
super(name, style);
}
public static MyConverter newInstance(final String[] options) {
return new MyConverter ("requestData", "requestData");
}
#Override
public void format(final LogEvent event, final StringBuilder toAppendTo) {
toAppendTo.append(getRequestId());
}
private String getRequestData() {
String data= // some static method call
if (data== null) {
data= "-";
}
return data;
}
}
If i was configurating log4j2 via xml config file, this would get picked up automaticly and configured for me. However since i am configurating it programmatically - how do i add this converted to my layout? I see no method of doing it.
Thanks for help!
This should be equivalent to scanning the plugins under the specified package:
PluginManager.addPackage("yourpackage");
I am new to DAML, I wanted to query all the active contracts using Java binding, Bot API and keep them into DB (or in-memory) for future query.
As per the docs, LedgerView can keep track of active contracts in-memory. However I am not able to successfully stream the active contracts.
You can find my code here, https://github.com/agrawald/daml-java-bot.
The above code have a schedule task which I am not very proud of.
The code for the class where I create DamlLedgerClient and start a schedule job to trigger the Bot. Please note
#Slf4j
#Service
#RequiredArgsConstructor
public class DamlContractSvc implements InitializingBean {
#Value("${daml.host}")
private String host;
#Value("${daml.port}")
private int port;
#Value("${daml.appId}")
private String appId;
#Value("${daml.party}")
private String party;
#Value("${daml.packageId}")
private String packageId;
#Autowired(required = true)
private ContractCache contractCache;
private DamlLedgerClient client;
#Scheduled(fixedDelay = 5000)
public void fetch() {
final TransactionFilter transactionFilter = new FiltersByParty(
Collections.singletonMap(party, NoFilter.instance));
Bot.wire(appId, client, transactionFilter, (ledgerView) -> Flowable.empty(),
contractCache);
}
#Override
public void afterPropertiesSet() throws Exception {
client = DamlLedgerClient.forHostWithLedgerIdDiscovery(host, port, Optional.empty());
client.connect();
}
}
I believe I should be running some Command at (ledgerView) -> Flowable.empty().
contractCache is a class which takes CreatedContract object and load it in the cache.
I may be doing something entirely wrong. please correct me.
I ditched the Bot approach and started using TransactionClient referring to the way Bot.wire method is implemented. Following is what my implementation looks like
#Slf4j
#Service
#RequiredArgsConstructor
public class DamlContractSvc implements InitializingBean {
#Value("${daml.host}")
private String host;
#Value("${daml.port}")
private int port;
#Value("${daml.appId}")
private String appId;
#Value("${daml.party}")
private String party;
#Value("${daml.packageId}")
private String packageId;
#Autowired(required = true)
private ContractRepo contractRepo;
private DamlLedgerClient client;
private final static AtomicReference<LedgerOffset> OFFSET = new AtomicReference<>(
LedgerOffset.LedgerBegin.getInstance());
#Scheduled(fixedDelay = 5000)
public void fetch() {
final TransactionFilter transactionFilter = new FiltersByParty(
Collections.singletonMap(party, NoFilter.instance));
client.getTransactionsClient().getTransactions(OFFSET.get(), transactionFilter, true).flatMapIterable(t -> {
OFFSET.set(new LedgerOffset.Absolute(t.getOffset()));
return t.getEvents();
}).forEach(contractRepo);
}
#Override
public void afterPropertiesSet() throws Exception {
client = DamlLedgerClient.forHostWithLedgerIdDiscovery(host, port, Optional.empty());
client.connect();
}
}
I am keeping track of OFFSET and fetching everything starting from LedgerOffset.LedgerBegin.
Full codebase is here: https://github.com/agrawald/daml-java-bot.
I'm trying to inject my dao object in controller. I've done this:
I've a:
1. MongoDBHelper
2. MerchantDAO
3. MerchantService
4. MerchantController
This is MongoDBHelper class:
import javax.inject.Singleton;
#Singleton
public class MongoDBHelper {
private DB db;
private Datastore datastore;
private Configuration config = Play.application().configuration();
private final String SERVER_URL = config.getString("server_url");
private final String USERNAME = config.getString("database.userName");
private final String PASSWORD = config.getString("database.password");
private final String DATABASE_NAME = config.getString("database.name");
public MongoDBHelper() {
try {
MongoClient mongoClient = new MongoClient();
this.db = mongoClient.getDB(DATABASE_NAME);
this.db.authenticate(USERNAME, PASSWORD.toCharArray());
Morphia morphia = new Morphia();
this.datastore = morphia.createDatastore(mongoClient, DATABASE_NAME);
morphia.mapPackage("models");
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
public DB getDB() {
return this.db;
}
public Datastore getDatastore() {
return this.datastore;
}
}
This is MerchantDAO class
public class MerchantDAO {
#Inject MongoDBHelper mongoDBHelper;
private Datastore datastore = mongoDBHelper.getDatastore();
private DB db = mongoDBHelper.getDB();
private static final String AUTH_TOKEN = "authToken";
private static final Config config = ConfigFactory.load(Play.application().configuration().getString("property.file.name"));
public void updateMerchantWithAuthToken(Merchant merchant){
Query<Merchant> query = datastore.createQuery(Merchant.class).field(config.getString("string.email")).equal(merchant.getEmail());
UpdateOperations<Merchant> ops = datastore.createUpdateOperations(Merchant.class).set(AUTH_TOKEN, merchant.getAuthToken()).set("lastRequestTime",merchant.getLastRequestTime());
UpdateResults res = datastore.update(query, ops);
}
}
}
This is MerchantService class:
public class MerchantService {
static final Config config = ConfigFactory.load(Play.application().configuration().getString("property.file.name"));
#Inject
MerchantDAO merchantDAO;
// Creating unique authToken for already logged in merchant
public String createToken(Merchant merchant) {
merchantDAO.updateMerchantWithAuthToken(merchant);
return authToken;
}
}
This is MerchantController
import javax.inject.Inject;
public class MerchantController extends Controller {
#Inject MerchantService merchantService;
public final static String AUTH_TOKEN_HEADER = "X-AUTH-TOKEN";
public static final String AUTH_TOKEN = "authToken";
public static final Config config = ConfigFactory.load(Play.application().configuration().getString("property.file.name"));
public static Merchant getMerchant() {
return (Merchant)Http.Context.current().args.get("merchant");
}
public Result login() throws Exception {
// code to perform login
return ok(); // status success / failure
}
}
I'm getting following error:
ProvisionException: Unable to provision, see the following errors:
1) Error injecting constructor, java.lang.NullPointerException
at daos.MerchantDAO.<init>(MerchantDAO.java:22)
while locating daos.MerchantDAO
for field at services.MerchantService.merchantDAO(MerchantService.java:26)
while locating services.MerchantService
for field at controllers.MerchantController.merchantService(MerchantController.java:21)
while locating controllers.MerchantController
for parameter 2 at router.Routes.<init>(Routes.scala:36)
while locating router.Routes
while locating play.api.inject.RoutesProvider
while locating play.api.routing.Router
1 error
What am I possibly doing wrong? Why is DI not working properly?
Thanks in advance.
I think the problem is with these lines:
private Datastore datastore = mongoDBHelper.getDatastore();
private DB db = mongoDBHelper.getDB();
These are evaluated during the object instance's construction. I believe that injection won't occur until AFTER the object instance has completed construction. Therefore, mongoDBHelper is null while the above assignments are made.
One way to solve this would be to set datastore and db in the method updateMerchantWithAuthToken.
The problem is that you are trying to access the Configuration object during the MongoDBHelper instantiation. You should just inject the play Configuration object to your module's constructor and initialize all properties within the constructor:
#Inject
public MongoDBHelper(Configuration configuration) {
config = Play.application().configuration();
<read the rest of the config values here>
See the note in the configurable bindings section of the D.I. documentation here