#Value can not generate value properly, getting null - spring

When I run the code the external configuration in the application.properties file does not get populated into the variable within the DataBucketUtil. I'm sure I'm doing something stupid,but I can not find out wheres the problem.
public class DataBucketUtil {
private static final Logger logger = LoggerFactory.getLogger(DataBucketUtil.class);
#Value("${gcp.config.file}")
private String gcpConfigFile;
#Value("${gcp.project.id}")
private String gcpProjectId;
#Value("${gcp.bucket.id}")
private String gcpBucketId;
#Value("${gcp.directory.name}")
private String gcpDirectoryName;
/**
* Upload file to GCS
*
* #param multipartFile-
* #param fileName-
* #param contentType-
* #return -
*/
public FileDto uploadFile(MultipartFile multipartFile, String fileName, String contentType) {
try {
logger.debug("Start file uploading process on GCS");
byte[] fileData = FileUtils.readFileToByteArray(convertFile(multipartFile));
InputStream inputStream = new ClassPathResource(gcpConfigFile).getInputStream();
StorageOptions options = StorageOptions.newBuilder().setProjectId(gcpProjectId)
.setCredentials(GoogleCredentials.fromStream(inputStream)).build();
Storage storage = options.getService();
Bucket bucket = storage.get(gcpBucketId, Storage.BucketGetOption.fields());
RandomString id = new RandomString(6, ThreadLocalRandom.current());
Blob blob = bucket.create(gcpDirectoryName + "/"
+ fileName + "-" + id.nextString() + checkFileExtension(fileName),
fileData, contentType);
if (blob != null) {
logger.debug("File successfully uploaded to GCS");
return new FileDto(blob.getName(), blob.getMediaLink());
}
} catch (IOException e) {
logger.error("An error occurred while uploading data. Exception: ", e);
throw new RuntimeException("An error occurred while uploading data to GCS");
}
throw new RuntimeException("An error occurred while uploading data to GCS");
}
My application properties is given below:
gcp.config.file=gcp-config/gcs-prod-ho-finance.json
gcp.project.id=brac-main gcp.bucket.id=prod-ho-finance
gcp.dir.name=gs://prod-ho-finance

It is not entirely clear from your code snippet but my guess would be that your DataBucketUtil is not instantiated as a Bean and therefore the #Value annotated fields are not populated. See here for more details about the #Value annotation.
You could transform your class to a service or component with the #Component or #Service annotation and then autowire it to where you need it. See here for more information about beans.

Please add Annotations. I hope it work.
#EnableConfigurationProperties
#Component
public class DataBucketUtil {
private static final Logger logger = LoggerFactory.getLogger(DataBucketUtil.class);
#Value("${gcp.config.file}")
private String gcpConfigFile;
#Value("${gcp.project.id}")
private String gcpProjectId;
#Value("${gcp.bucket.id}")
private String gcpBucketId;
#Value("${gcp.directory.name}")
private String gcpDirectoryName;
/** ............ **/
}

Related

Spring Integration MimeMessage gmail Folder is not Open exception

Whenever I try to read content of a MimeMessage I get the "java.lang.IllegalStateException: Folder is not Open" exception.
I have a very simple service handling the received message:
#Service
public class ReceiveMailService {
private final Logger log = LoggerFactory.getLogger(ReceiveMailService.class);
public void handleReceivedMail(MimeMessage receivedMessage) {
try {
log.debug("{}", receivedMessage.getContent());
MimeMessageParser mimeMessageParser = new MimeMessageParser(receivedMessage).parse(); // it breaks here
doMyStuff(mimeMessageParser);
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
}
Here's my configuration class:
#Configuration
#EnableIntegration
public class MailReceiverConfiguration {
private static final Logger log = LoggerFactory.getLogger(MailReceiverConfiguration.class);
#Value("${spring.mail.pop3.host}")
private String host;
#Value("${spring.mail.pop3.port}")
private Integer port;
#Value("${spring.mail.username}")
private String username;
#Value("${spring.mail.password}")
private String password;
private final ReceiveMailService receiveMailService;
public MailReceiverConfiguration(ReceiveMailService receiveMailService) {
this.receiveMailService = receiveMailService;
}
#Bean
public IntegrationFlow mailListener() {
return IntegrationFlows
.from(Mail
.pop3InboundAdapter(host, port, username, password)
.javaMailProperties(p -> {
p.put("mail.debug", "false");
p.put("mail.pop3.socketFactory.fallback", "false");
p.put("mail.pop3.port", port);
p.put("mail.pop3.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
p.put("mail.pop3.socketFactory.port", port);
})
.maxFetchSize(10)
.shouldDeleteMessages(false),
e -> e.poller(Pollers.fixedRate(5000).maxMessagesPerPoll(10))
)
.handle(message -> receiveMailService.handleReceivedMail((MimeMessage) message.getPayload()))
.get();
}
}
I have ran out of ideas why this wouldn't work.
See this option for use-cases like yours:
/**
* When configured to {#code false}, the folder is not closed automatically after a fetch.
* It is the target application's responsibility to close it using the
* {#link org.springframework.integration.IntegrationMessageHeaderAccessor#CLOSEABLE_RESOURCE} header
* from the message produced by this channel adapter.
* #param autoCloseFolder set to {#code false} to keep folder opened.
* #return the spec.
* #since 5.2
* #see AbstractMailReceiver#setAutoCloseFolder(boolean)
*/
public S autoCloseFolder(boolean autoCloseFolder) {
The docs is here: https://docs.spring.io/spring-integration/docs/current/reference/html/mail.html#mail-inbound
Starting with version 5.2, the autoCloseFolder option is provided on the mail receiver. Setting it to false doesn’t close the folder automatically after a fetch, but instead an IntegrationMessageHeaderAccessor.CLOSEABLE_RESOURCE header (see MessageHeaderAccessor API for more information) is populated into every message to producer from the channel adapter.

spring: customizing the authorizationEndpoint (OAuth2)

I am trying to customize the code of the spring oauth authorization server.
for now I have just copied the framework authorizationEndpoint code and placed it in another class. I just changed the address mapping to /custom/oauth/authorize. I have also added #Controller before the class declaration otherwise this code will not be used at all:
#Controller
//#Order(Ordered.HIGHEST_PRECEDENCE)
#SessionAttributes("authorizationRequest")
public class AuthorizationEndpointCustom extends AuthorizationEndpoint {
#Autowired
private AuthenticationManager authenticationManager;
private AuthorizationCodeServices authorizationCodeServices = new InMemoryAuthorizationCodeServices();
private RedirectResolver redirectResolver = new DefaultRedirectResolver();
private UserApprovalHandler userApprovalHandler = new DefaultUserApprovalHandler();
private SessionAttributeStore sessionAttributeStore = new DefaultSessionAttributeStore();
private OAuth2RequestValidator oauth2RequestValidator = new DefaultOAuth2RequestValidator();
private String userApprovalPage = "forward:/oauth/confirm_access";
private String errorPage = "forward:/oauth/error";
private Object implicitLock = new Object();
public void setSessionAttributeStore(SessionAttributeStore sessionAttributeStore) {
this.sessionAttributeStore = sessionAttributeStore;
}
public void setErrorPage(String errorPage) {
this.errorPage = errorPage;
}
#RequestMapping(value = "/oauth/authorize")
public ModelAndView authorize(Map<String, Object> model, #RequestParam Map<String, String> parameters,
SessionStatus sessionStatus, Principal principal) {
System.out.println("\n\ninside custom authorization endpoint");
// Pull out the authorization request first, using the OAuth2RequestFactory. All further logic should
// query off of the authorization request instead of referring back to the parameters map. The contents of the
// parameters map will be stored without change in the AuthorizationRequest object once it is created.
AuthorizationRequest authorizationRequest = getOAuth2RequestFactory().createAuthorizationRequest(parameters);
Set<String> responseTypes = authorizationRequest.getResponseTypes();
if (!responseTypes.contains("token") && !responseTypes.contains("code")) {
throw new UnsupportedResponseTypeException("Unsupported response types: " + responseTypes);
}
if (authorizationRequest.getClientId() == null) {
throw new InvalidClientException("A client id must be provided");
}
try {
if (!(principal instanceof Authentication) || !((Authentication) principal).isAuthenticated()) {
throw new InsufficientAuthenticationException(
"User must be authenticated with Spring Security before authorization can be completed.");
}
ClientDetails client = getClientDetailsService().loadClientByClientId(authorizationRequest.getClientId());
// The resolved redirect URI is either the redirect_uri from the parameters or the one from
// clientDetails. Either way we need to store it on the AuthorizationRequest.
String redirectUriParameter = authorizationRequest.getRequestParameters().get(OAuth2Utils.REDIRECT_URI);
String resolvedRedirect = redirectResolver.resolveRedirect(redirectUriParameter, client);
if (!StringUtils.hasText(resolvedRedirect)) {
throw new RedirectMismatchException(
"A redirectUri must be either supplied or preconfigured in the ClientDetails");
}
authorizationRequest.setRedirectUri(resolvedRedirect);
// We intentionally only validate the parameters requested by the client (ignoring any data that may have
// been added to the request by the manager).
oauth2RequestValidator.validateScope(authorizationRequest, client);
// Some systems may allow for approval decisions to be remembered or approved by default. Check for
// such logic here, and set the approved flag on the authorization request accordingly.
authorizationRequest = userApprovalHandler.checkForPreApproval(authorizationRequest,
(Authentication) principal);
// TODO: is this call necessary?
boolean approved = userApprovalHandler.isApproved(authorizationRequest, (Authentication) principal);
authorizationRequest.setApproved(approved);
// Validation is all done, so we can check for auto approval...
if (authorizationRequest.isApproved()) {
if (responseTypes.contains("token")) {
return getImplicitGrantResponse(authorizationRequest);
}
if (responseTypes.contains("code")) {
return new ModelAndView(getAuthorizationCodeResponse(authorizationRequest,
(Authentication) principal));
}
}
// Place auth request into the model so that it is stored in the session
// for approveOrDeny to use. That way we make sure that auth request comes from the session,
// so any auth request parameters passed to approveOrDeny will be ignored and retrieved from the session.
model.put("authorizationRequest", authorizationRequest);
return getUserApprovalPageResponse(model, authorizationRequest, (Authentication) principal);
}
catch (RuntimeException e) {
sessionStatus.setComplete();
throw e;
}
}
private OAuth2AccessToken getAccessTokenForImplicitGrant(TokenRequest tokenRequest,
OAuth2Request storedOAuth2Request) {
OAuth2AccessToken accessToken = null;
// These 1 method calls have to be atomic, otherwise the ImplicitGrantService can have a race condition where
// one thread removes the token request before another has a chance to redeem it.
synchronized (this.implicitLock) {
accessToken = getTokenGranter().grant("implicit",
new ImplicitTokenRequest(tokenRequest, storedOAuth2Request));
}
return accessToken;
}
.
.
.
I have also instructed the framework to change the mappring from /oauth/authorize to /custom/oauth/authorize:
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager).tokenStore(tokenStore())
.accessTokenConverter(tokenEnhancer()).pathMapping("/oauth/authorize", "/custom/authorize/");
}
but when I run the code I encounter the following error:
Description:
Field tokenGranter in com.example.demo.controller.AuthorizationEndpointCustom required a bean of type 'org.springframework.security.oauth2.provider.TokenGranter' that could not be found.
The injection point has the following annotations:
- #org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type 'org.springframework.security.oauth2.provider.TokenGranter' in your configuration.
the parent class of AuthorizationEndpoint (AbstractEndpoint) declares tokenGranter but it is not instantiated. there is no #autowired for this and other attributes of this class. who does genereate and inject these variable into this class?
how can I get hold of tokenGranter obj and inject it?
/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.oauth2.provider.endpoint;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.oauth2.provider.OAuth2RequestFactory;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.TokenGranter;
import org.springframework.security.oauth2.provider.error.DefaultWebResponseExceptionTranslator;
import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;
import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory;
import org.springframework.util.Assert;
/**
* #author Dave Syer
*
*/
public class AbstractEndpoint implements InitializingBean {
protected final Log logger = LogFactory.getLog(getClass());
private WebResponseExceptionTranslator providerExceptionHandler = new DefaultWebResponseExceptionTranslator();
private TokenGranter tokenGranter;
private ClientDetailsService clientDetailsService;
private OAuth2RequestFactory oAuth2RequestFactory;
private OAuth2RequestFactory defaultOAuth2RequestFactory;
public void afterPropertiesSet() throws Exception {
Assert.state(tokenGranter != null, "TokenGranter must be provided");
Assert.state(clientDetailsService != null, "ClientDetailsService must be provided");
defaultOAuth2RequestFactory = new DefaultOAuth2RequestFactory(getClientDetailsService());
if (oAuth2RequestFactory == null) {
oAuth2RequestFactory = defaultOAuth2RequestFactory;
}
}
public void setProviderExceptionHandler(WebResponseExceptionTranslator providerExceptionHandler) {
this.providerExceptionHandler = providerExceptionHandler;
}
public void setTokenGranter(TokenGranter tokenGranter) {
this.tokenGranter = tokenGranter;
}
protected TokenGranter getTokenGranter() {
return tokenGranter;
}
protected WebResponseExceptionTranslator getExceptionTranslator() {
return providerExceptionHandler;
}
protected OAuth2RequestFactory getOAuth2RequestFactory() {
return oAuth2RequestFactory;
}
protected OAuth2RequestFactory getDefaultOAuth2RequestFactory() {
return defaultOAuth2RequestFactory;
}
public void setOAuth2RequestFactory(OAuth2RequestFactory oAuth2RequestFactory) {
this.oAuth2RequestFactory = oAuth2RequestFactory;
}
protected ClientDetailsService getClientDetailsService() {
return clientDetailsService;
}
public void setClientDetailsService(ClientDetailsService clientDetailsService) {
this.clientDetailsService = clientDetailsService;
}
}
I am answering my own question.
I took a good look at the framework code and I found out that AuthorizationServerEndpointsConfiguration class creates an object of type AuthorizationEndpoint and populates it's attributes and then return this object as a bean.
I managed to solve above mentioned problem with TokenGranter by creating a bean of my new AuthorizationEndpointCustom the same way AuthorizationServerEndpointsConfiguration does. this is the code to do so:
#Autowired
private ClientDetailsService clientDetailsService;
#Autowired
AuthorizationServerEndpointsConfiguration asec;
#Bean
#Order(value = Ordered.HIGHEST_PRECEDENCE)
#Primary
public AuthorizationEndpoint authorizationEndpoint () throws Exception{
AuthorizationEndpointCustom authorizationEndpoint = new AuthorizationEndpointCustom();
FrameworkEndpointHandlerMapping mapping = asec.getEndpointsConfigurer().getFrameworkEndpointHandlerMapping();
authorizationEndpoint.setUserApprovalPage(extractPath(mapping, "/oauth/confirm_access"));
authorizationEndpoint.setProviderExceptionHandler(asec.getEndpointsConfigurer().getExceptionTranslator());
authorizationEndpoint.setErrorPage(extractPath(mapping, "/oauth/error"));
authorizationEndpoint.setTokenGranter(asec.getEndpointsConfigurer().getTokenGranter());
authorizationEndpoint.setClientDetailsService(clientDetailsService);
authorizationEndpoint.setAuthorizationCodeServices(asec.getEndpointsConfigurer().getAuthorizationCodeServices());
authorizationEndpoint.setOAuth2RequestFactory(asec.getEndpointsConfigurer().getOAuth2RequestFactory());
authorizationEndpoint.setOAuth2RequestValidator(asec.getEndpointsConfigurer().getOAuth2RequestValidator());
authorizationEndpoint.setUserApprovalHandler(asec.getEndpointsConfigurer().getUserApprovalHandler());
return authorizationEndpoint;
}
private String extractPath(FrameworkEndpointHandlerMapping mapping, String page) {
String path = mapping.getPath(page);
if (path.contains(":")) {
return path;
}
return "forward:" + path;
}
but this did not result in what I hoped to. the new bean does not replace the bean from framework code. this situation with overriding beans led to another question:
how replace framework beans
buttom line, this is not the way to override the framework endpoints. you can simply create a controller with mappings for these endpoints (e.g /oauth/authorize or /oauth/token). automatically these mappings will get precedence over framework endpoints. for more info refer to spring doc

Spring profiles not getting resolved when using it with the spring web based project

in application.properties given : spring.profiles.active=DEV
and in dev config file : mentioned all the mongo connection properties
and added the configuration java file like
#Configuration
#PropertySource("classpath:userIdentity_Dev.properties")
#Profile("DEV")
public class UserIdentityConfigDev
{
}
when running the application the spring profiler is not getting resolved the
below stack trace is received
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userIdentityService': Unsatisfied dependency expressed through field 'userIdentityBusiness'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userIdentityBusiness': Unsatisfied dependency expressed through field 'userIdentityRepository'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userIdentityRepositoryImpl': Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'mongodb.userIdentity.host' in string value "${mongodb.userIdentity.host}"
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:588)
Saying that the ${mongodb.userIdentity.host} property is not resolved
when creating war and jar file for the project the spring profile is not resolved
This is main class:
` #SpringBootApplication(exclude= {DataSourceAutoConfiguration.class ,MongoAutoConfiguration.class, MongoDataAutoConfiguration.class})
#PropertySource("classpath:application.properties")
public class ApplicationStart extends SpringBootServletInitializer
{
public static void main(String[] args)
{
SpringApplication.run(ApplicationStart.class,args);
}
}`
below is the property file:
## MongoDB Connection Properties-----------------
MongoDB database
mongodb.userIdentity.database = UserIdentity_CS
isConnectionStringUsed is true then application creates connection as per connectionString else it will use MongoDB single server properties.
mongodb.userIdentity.isConnectionStringUsed = false
connectionString with authentication
mongodb.connectionString = mongodb://sa:Test%40123#SPT-CPU-0259:27017,SPT-CPU-0173:27017/admin?replicaSet=surveillens
connectionString without authentication
mongodb.userIdentity.connectionString = mongodb://localhost:27017/?replicaSet=surveillens
MongoDB single server properties.
mongodb.userIdentity.host = localhost
mongodb.userIdentity.port = 27017
Authentication properties
mongodb.userIdentity.isAuthenticationEnable = false
mongodb.userIdentity.userName = sa
mongodb.userIdentity.password = Test#123
mongodb.userIdentity.authDB = admin
Collection Name for user Identity
mongodb.userIdentity.collectionName = CreditScore
Other properties -----------------------
userIdentity.ValidKeySet = email;phonenumber;_id
userIdentity.logsFolder = ./IdentityLogs/
userIdentity.insertBatchSize = 100
and below is the file .java file where all this properties are used
`
#Configuration
public abstract class MongoDbRepository {
private Class<T> clazz;
private static MongoClient mongoClient = null;
private static MongoDatabase mongoDatabase = null;
private static ObjectMapper mapper = null;
#Value("${mongodb.userIdentity.host}")
private String mongoHost;
#Value("${mongodb.userIdentity.port}")
private int mongoPortNumber;
#Value("${mongodb.userIdentity.database}")
private String mongoDatabaseName;
#Value("${mongodb.userIdentity.userName}")
private String mongoUserName;
#Value("${mongodb.userIdentity.authDB}")
private String mongoAuthDB;
#Value("${mongodb.userIdentity.password}")
private String mongoPassword;
#Value("${mongodb.userIdentity.isAuthenticationEnable}")
private boolean mongoIsAuthEnable;
#Value("${mongodb.userIdentity.isConnectionStringUsed}")
private boolean mongoIsConnectionStringUsed;
#Value("${mongodb.userIdentity.connectionString}")
private String mongoConnectionString;
public final void setClazz(Class<T> clazzToSet)
{
this.clazz = clazzToSet;
}
/**
* Instantiates a new mongo base repository.
* #throws Exception
*/
public MongoDbRepository()
{
//Trigger MongoDB Connection initialization
if(mongoClient == null)
{
prepareMongoConnection();
}
else
{
// Trigger any method to check MongoDB client is connected
mongoClient.getAddress();
}
// Trigger ObjectMapper initialization
if(mapper == null)
prepareObjectMapper();
}
/**
* Instantiates a new mongoDB connection.
* #throws Exception
*/
private void prepareMongoConnection()
{
if (mongoConnectionString != null && !mongoConnectionString.isEmpty())
{
boolean isConnectionStringUsed = mongoIsConnectionStringUsed;
if(isConnectionStringUsed)
{
MongoClientURI clientUri = new MongoClientURI(mongoConnectionString);
mongoClient = new MongoClient(clientUri);
}
else
{
if(mongoIsAuthEnable)
{
MongoCredential credential = MongoCredential.createCredential(mongoUserName, mongoAuthDB, mongoPassword.toCharArray());
mongoClient = new MongoClient( new ServerAddress(mongoHost, mongoPortNumber), Arrays.asList(credential));
}
else
mongoClient = new MongoClient(mongoHost, mongoPortNumber);
}
// Trigger any method to check MongoDB client is connected
mongoClient.getAddress();
// Get Database from mongoClient.
mongoDatabase = mongoClient.getDatabase(mongoDatabaseName);
}
}
/**
* Get an objectMapper.
*/
private void prepareObjectMapper()
{
mapper = CommonFunctions.getObjectMapper();
}
/**
* Get the MongoDB collection object from MongoDB.
*
* #param collectionName is Name of a MongoDB collection
* #return Collection object
* #throws Exception
*/
private MongoCollection<Document> getCollection(String collectionName) throws Exception
{
if(mongoClient == null)
prepareMongoConnection();
return mongoDatabase.getCollection(collectionName);
}
/* ------- Find functions ------- */
/**
* Find one documents from mongoDB collection.
*
* #param collectionName the collection name
* #param query the query document - set to empty document means no query filtering.
*
* #return entityObj the entity Object
* #throws Exception the exception
*/
public T findOne(String collectionName, Object query) throws Exception
{
if(clazz == null)
throw new NullPointerException("ST224 - Generic class is null - set the generic class before perform MongoDB operation");
MongoCollection<Document> collection = getCollection(collectionName);
Document mongoDoc = collection.find(convertToBsonDocument(query)).first();
String jsonStr = mapper.writeValueAsString(mongoDoc);
T entityObj = mapper.readValue(jsonStr, clazz);
return entityObj;
}
}`

log4j2 configuration from file

I am working on an existing system that uses log4j, I want to update to log4j2.
There is a custom spring bean that loads the configuration from a file. I need to keep this approach. I cannot use the "log4j.configurationFile" system property.
We have a properties file where the path to the current log4j.xml is specified ( NFS share )
The spring bean has this code ...
public class Log4jConfigurationBean implements ResourceLoaderAware,
InitializingBean {
private ResourceLoader resourceLoader;
private boolean enabled;
private String location;
/**
* Default, no argument constructor.
*/
public Log4jConfigurationBean() {
enabled = true;
}
/**
* Sets whether or not this bean should load an external configuration
* defined by {#link #setLocation(Resource)}. If <code>false</code>, this
* bean does nothing.
*
* <p>
* Default value is <code>true</code>.
* </p>
*
* #param enabled
* <code>false</code> causes this bean to do nothing
*/
public void setEnabled(final boolean enabled) {
this.enabled = enabled;
}
/**
* Sets the location of the external log4j configuration (xml or properties)
* to be loaded.
*
* #param location
* the location of the external configuration to be loaded.
* #throws IllegalStateException
* if there is a problem resolving the location resource
* #throws NullPointerException
* if <code>resource</code> is <code>null</code>
*/
public void setLocation(final String location) {
this.location = StringUtils.trimToNull(location);
}
#Override
public void setResourceLoader(final ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
/**
* #throws IllegalStateException
* if enabled and no location has be set, or if the external
* configuration is neither xml or properties.
*/
#Override
public void afterPropertiesSet() throws Exception {
URL configURL = null;
if (null != location) {
try {
final Resource resource = resourceLoader.getResource(location);
if (null != resource) {
configURL = resource.getURL();
}
} catch (IOException e) {
throw new IllegalArgumentException(
"Could not resolve configuration location due to error: ",
e);
}
}
if (enabled && null == configURL) {
throw new IllegalStateException(
"Log4j configuration enabled, but configuration location is not set.");
}
if (enabled) {
if (configURL.getFile().toLowerCase().endsWith(".xml")) {
DOMConfigurator.configure(configURL);
} else if (configURL.getFile().toLowerCase()
.endsWith(".properties")) {
PropertyConfigurator.configure(configURL);
} else {
throw new IllegalStateException(
"Configuration must be properties or xml: "
+ configURL.getFile());
}
}
}
}
In log4j2 there is no PropertyConfigurator.
How can I load the log4j2.xml file the same way.
The file path to the log4j2.xml file is specified in a spring property file.
The goal is to have the war files contain a log4j2.xml file in the classpath. This will be used when developing on your local box.
When the web apps are deployed to a qa environment, there is a property file containing the following key/value pair...
# Should an external file be used for log4j configuration
log4j.enabled=true
log4j.location=file:/paht to log4j2.xml
A spring bean is using these values to decide if an external log4j2.xml file should be used instead of the one on the classpath.
I tried with a spring bean like this... the code is executed, but it still uses the configuration file on the classpath.
public class Log4j2ConfigurationBean implements ResourceLoaderAware, InitializingBean {
private static final Logger log = LoggerFactory.getLogger(Log4j2ConfigurationBean.class);
private ResourceLoader resourceLoader;
private boolean enabled;
private String location;
/**
* Default, no argument constructor.
*/
public Log4j2ConfigurationBean() {
enabled = true;
}
/**
* Sets whether or not this bean should load an external configuration defined by {#link #setLocation(Resource)}. If <code>false</code>, this bean does nothing.
*
* <p>
* Default value is <code>true</code>.
* </p>
*
* #param enabled
* <code>false</code> causes this bean to do nothing
*/
public void setEnabled(final boolean enabled) {
this.enabled = enabled;
}
/**
* Sets the location of the external log4j configuration (xml or properties) to be loaded.
*
* #param location
* the location of the external configuration to be loaded.
* #throws IllegalStateException
* if there is a problem resolving the location resource
* #throws NullPointerException
* if <code>resource</code> is <code>null</code>
*/
public void setLocation(final String location) {
this.location = StringUtils.trimToNull(location);
}
#Override
public void setResourceLoader(final ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
/**
* #throws IllegalStateException
* if enabled and no location has be set, or if the external configuration is neither xml or properties.
*/
#Override
public void afterPropertiesSet() throws Exception {
URL configURL = null;
if (enabled) {
if (StringUtils.isBlank(location)) {
throw new IllegalStateException("Log4j2 configuration enabled, but configuration location is not set.");
}
try {
System.out.println(this.getClass().getName() + " : Loading log4j2 configuration with " + location);
final Resource resource = resourceLoader.getResource(location);
if (null != resource) {
configURL = resource.getURL();
}
} catch (IOException e) {
throw new IllegalArgumentException("Could not resolve configuration location due to error: ", e);
}
if (configURL.getFile().toLowerCase().endsWith(".xml")) {
try {
System.setProperty("Log4jContextSelector", "org.apache.logging.log4j.core.async.AsyncLoggerContextSelector");
System.setProperty("AsyncLogger.RingBufferSize", "8192");
ConfigurationFactory configurationFactory = XmlConfigurationFactory.getInstance();
ConfigurationSource configurationSource = new ConfigurationSource(configURL.openStream(), configURL);
Configuration configuration = configurationFactory.getConfiguration(configurationSource);
configuration.start();
log.info("Log4j2 configured with {}", location);
log.info("System property Log4jContextSelector set to {}", System.getProperty("Log4jContextSelector"));
log.info("System property AsyncLogger.RingBufferSize set to {}", System.getProperty("AsyncLogger.RingBufferSize"));
} catch (Exception e) {
System.out.println(this.getClass().getName() + " : Could not initialize log4j2 with resource " + location);
System.out.println(e.getStackTrace());
}
} else {
throw new IllegalStateException("Configuration must be xml: " + configURL.getFile());
}
} else {
System.out.println(this.getClass().getName() + " : External log4j2 configuration not configured.");
}
}
}
Thanks.
Check out the How do I configure log4j2 in code without a configuration file? section here - http://logging.apache.org/log4j/2.x/faq.html

Externalized message.properties file is not picking up in Tomcat

According to my requirement, I need to externalize the message.properties file (Keep outside from war file) and in same time it should be automatically re loadable on update.
So I achieved both by following code and its working fine with Jetty Server. But in when I use Tomcat Server that externalized property file is not picking up by the system, instead its uses only the file inside the war.
public final class Messages
{
public static final String BUNDLE_NAME = "com.sample.project.core.ui.resources.messages";
// private static ResourceBundle resourceBundle = ResourceBundle.getBundle(BUNDLE_NAME);
private static ResourceBundle resourceBundle;
private static final Logger LOGGER = Logger.getLogger(Messages.class);
private static ReloadableResourceBundleMessageSource messageSource;
static
{
try
{
FileInputStream fis =
new FileInputStream(System.getProperty("resources.messages.file.path"));
resourceBundle = new PropertyResourceBundle(fis);
}
catch (FileNotFoundException e)
{
LOGGER.error("messages.properties file not found: " + e);
resourceBundle = ResourceBundle.getBundle(BUNDLE_NAME);
}
catch (Exception e)
{
LOGGER.error("messages.properties file reading failed: " + e);
resourceBundle = ResourceBundle.getBundle(BUNDLE_NAME);
}
}
private Messages()
{
}
/**
* <p>
* setter methos to ReloadableResourceBundleMessageSource object.
* </p>
*
*
* #param inMessageSource
* set reloadable resources bundle
**/
public static void setMessageSource(final ReloadableResourceBundleMessageSource inMessageSource)
{
Messages.messageSource = inMessageSource;
Messages.messageSource.setBasename(System.getProperty("resources.messages.file.path"));
}
/**
* <p>
* Resolve a message by a key and argument replacements.
* </p>
*
* #see MessageFormat#format(String, Object...)
* #param key
* the message to look up
* #param arguments
* optional message arguments
* #return the resolved message
**/
public static String getMessage(final String key, final Object... arguments)
{
try
{
if (messageSource != null)
{
return messageSource.getMessage(key, arguments, Locale.getDefault());
}
else
{
if (arguments != null)
return MessageFormat.format(resourceBundle.getString(key), arguments);
return resourceBundle.getString(key);
}
}
catch (NoSuchMessageException e)
{
LOGGER.error("Message key not found: " + key);
return '!' + key + '!';
}
catch (MissingResourceException e)
{
LOGGER.error("Message key not found: " + key);
return '!' + key + '!';
}
}
}
(Here file path I'm passing as a VM argument using "resources.messages.file.path" key)
First I thought it was a problem with accessing the file system and tried many ways. Then I heard about catalina.policy file and I added some lines like this..
grant codeBase "file:${catalina.base}/webapps/sample.war/-" {
permission java.security.AllPermission;
permission java.io.FilePermission "file:${catalina.base}${file.separator}webapps${file.separator}messages.properties", "read, write";
permission java.util.PropertyPermission "resources.messages.file.path", "read";
}
But non of them gave me luck. I'm desperate. Any idea what this issue is? Please help me. Thank you in advance. (Tested on Tomcat6)
Finally I found where I screwed up.
This setter method of messageSource should not be static and I removed static access of messageSource
messageSource = inMessageSource;
messageSource.setBasename(System.getProperty("resources.messages.file.path"));
Now code is working fine. And no need of that permission entry in catalina.policy file. Thanks to everyone who helped me.

Resources