Where and How To Define An Application Property? - JHIpster - jhipster

In Spring Boot, an application property can be defined in application.properties file. For example, a prefix of Rest can be defined as
spring.data.rest.basePath=api
For JHipster which is based on Spring Boot, I guess that an application property could defined in the application.yml file. But none of the follow approach work for me: a 404 error.
spring.data.rest.basePath: api
spring:
data:
rest:
basePath: api
The other possibility is the configuration itself doesn't work.

I have same problem and finally figured it out!
Quote from Jhipster website:
Your generated application can also have its own Spring Boot properties. This is highly recommended, as it allows type-safe configuration of the application, as well as auto-completion and documentation within an IDE.
JHipster has generated a ApplicationProperties class in the config package, which is already preconfigured, and it is already documented at the bottom the application.yml, application-dev.yml and application-prod.yml files. All you need to do is code your own specific properties.
In my case, I have set the properties in applicaiton-prod.yml
application:
redis:
host: vnode1
pool:
max-active: 8
max-idle: 8
max-wait: -1
min-idle: 0
port: 6379
In ApplicationProperties class:
#ConfigurationProperties(prefix = "application", ignoreUnknownFields = false)
public class ApplicationProperties {
public final Redis redis = new Redis();
public Redis getRedis() {
return redis;
}
public static class Redis {
private String host = "127.0.0.1";
private int port = 0;
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
private Pool pool = new Pool();
public void setPool(Pool pool) {
this.pool = pool;
}
public Pool getPool() {
return this.pool;
}
public static class Pool {
private int maxActive = 8;
private int maxWait = -1;
public int getMaxIdle() {
return maxIdle;
}
public void setMaxIdle(int maxIdle) {
this.maxIdle = maxIdle;
}
private int maxIdle = 8;
private int minIdle = 0;
public void setMaxActive(int maxActive) {
this.maxActive = maxActive;
}
public int getMaxActive() {
return maxActive;
}
public int getMinIdle() {
return minIdle;
}
public void setMinIdle(int minIdle) {
this.minIdle = minIdle;
}
public int getMaxWait() {
return maxWait;
}
public void setMaxWait(int maxWait) {
this.maxWait = maxWait;
}
}
}
}
Then I use it as:
private final ApplicationProperties.Redis redis;
public RedisConfiguration(ApplicationProperties applicationProperties){
redis = applicationProperties.getRedis();
}
For instance use max-wait and host:
this.redis.getPool().getMaxWait();
this.redis.getHost();
Hope it helps.

The application.yml should be whit spaces not tabs.
Try like this:
spring:
data:
rest:
basePath: api
In my application the file is in the path:
src\main\resources\config\application.yml

After trying dozens of time how to handle the problem I finally figured out how to make it work. Maybe it will be useful for somebody.
To use prefix for controllers (let's say for example jh) we need to use server.servlet.context-path and not spring.data.rest.basePath.
Link to documentation https://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html
So application.yml should looks like this:
server:
servlet:
context-path: /jh
session:
cookie:
http-only: true

The information in this link is also useful to help solve.
https://docs.spring.io/spring-boot/docs/1.0.1.RELEASE/reference/html/boot-features-external-config.html
After you have built you application config class as per Haifeng Zhang answer above you can then access these properties anywhere by:
#Autowired
private ApplicationProperties redis;
then to access the properties
int myEnvironmentPort = redis.getPort();

Related

System.IO.InvalidDataException : ServiceStackHost.Instance has already been set (BasicAppHost)

I'm getting an error when i try to run some tests on my servicestack web service.
I'm using ServiceStack 4.5.8 and Nunit 3.5. The solution was created initially from a ServiceStackVS template.
The error, which appears on a number of tests, is
System.IO.InvalidDataException : ServiceStackHost.Instance has already been set (BasicAppHost)</br>
TearDown : System.NullReferenceException : Object reference not set to an instance of an object.</br>
at ServiceStack.ServiceStackHost.Init()</br>
at MyApp.Tests.EchoServiceUnitTests.OneTimeSetup() in </br>
C:\Repos\MyApp\Myapp\MyApp.Tests\EchoServiceUnitTests.cs:line 45 </br>
--TearDown</br>
at MyApp.Tests.EchoServiceUnitTests.TestFixtureTearDown() in </br>C:\Repos\MyApp\MyApp\MyApp.Tests\EchoServiceUnitTests.cs:line 54
One of the tests that regularly generates this error is
namespace Tests
{
[TestFixture]
public class EchoServiceUnitTests
{
private ServiceStackHost appHost;
[OneTimeSetUp]
public void OneTimeSetup()
{
this.appHost = new BasicAppHost(typeof(EchoService).Assembly).Init();
}
[OneTimeTearDown]
public void TestFixtureTearDown()
{
this.appHost.Dispose();
}
[Test]
public void TestService()
{
const string Message = "Hello";
var service = this.appHost.Container.Resolve <EchoService>();
var response = (EchoResponse)service.Any(new Echo
{
Message = Message
});
Assert.That(response.Message,
Is.EqualTo(Message));
}
}
}
the service for this is
namespace ServiceInterface
{
public class EchoService : Service
{
public object Any(Echo request)
{
return new EchoResponse {Message = request.Message};
}
}
}
[Route("/Echo")]
[Route("/Echo/{Message}")]
public class Echo : IReturn<EchoResponse>
{
public string Message { get; set; }
}
public class EchoResponse : IHasResponseStatus
{
public EchoResponse()
{
this.ResponseStatus = new ResponseStatus();
}
public string Message { get; set; }
public ResponseStatus ResponseStatus { get; set; }
}
And finally my apphost
namespace MyApplication
{
using System;
using Funq;
using ServiceInterface;
using ServiceModel.Validators;
using ServiceStack;
using ServiceStack.Admin;
using ServiceStack.Api.Swagger;
using ServiceStack.Caching;
using ServiceStack.Configuration;
using ServiceStack.Logging;
using ServiceStack.Logging.NLogger;
using ServiceStack.MsgPack;
using ServiceStack.OrmLite;
using ServiceStack.OrmLite.SqlServer.Converters;
using ServiceStack.ProtoBuf;
using ServiceStack.Razor;
using ServiceStack.Validation;
using ServiceStack.VirtualPath;
using ServiceStack.Wire;
public class AppHost : AppHostBase
{
public static ILog Log = LogManager.GetLogger(typeof(AppHost));
public AppHost()
: base("MyApp",
typeof(HelloService).Assembly) { }
public override void Configure(Container container)
{
LogManager.LogFactory = new NLogFactory();
Log = LogManager.GetLogger(this.GetType());
this.Plugins.Add(new RazorFormat());
this.Plugins.Add(new PostmanFeature());
this.Plugins.Add(new SwaggerFeature());
this.Plugins.Add(new AdminFeature());
var ormSettings = new AppSettings();
container.Register <ICacheClient>(new MemoryCacheClient());
var dbFactory = new OrmLiteConnectionFactory(ormSettings.GetString("SqlDbConnection"),
SqlServerDialect.Provider);
dbFactory.RegisterConnection("Database2",
ormSettings.GetString("Sql2Connection"),
SqlServerDialect.Provider);
SqlServerDialect.Provider.RegisterConverter<DateTime?>(new SqlServerDateTimeConverter());
this.Plugins.Add(new RequestLogsFeature
{
RequestLogger = new CsvRequestLogger(files: new FileSystemVirtualPathProvider(this,
this.Config.WebHostPhysicalPath),
requestLogsPattern: "requestlogs/{year}-{month}/{year}-{month}-{day}.csv",
errorLogsPattern: "requestlogs/{year}-{month}/{year}-{month}-{day}-errors.csv",
appendEvery: TimeSpan.FromSeconds(1)),
EnableRequestBodyTracking = true,
EnableResponseTracking = true,
EnableErrorTracking = true,
});
this.Plugins.Add(new AutoQueryDataFeature
{
MaxLimit = 1000
});
this.Plugins.Add(new AutoQueryFeature());
var sse = new ServerEventsFeature
{
StreamPath = "/event-stream",
HeartbeatPath = "/event-heartbeat",
UnRegisterPath = "/event-unregister",
SubscribersPath = "/event-subscribers",
LimitToAuthenticatedUsers = false,
IdleTimeout = TimeSpan.FromSeconds(30),
HeartbeatInterval = TimeSpan.FromSeconds(10),
NotifyChannelOfSubscriptions = true,
};
this.Plugins.Add(sse);
Plugins.Add(new AdminFeature());
Plugins.Add(new WireFormat());
Plugins.Add(new MsgPackFormat());
Plugins.Add(new ProtoBufFormat());
}
}
}
I've tried a variety of suggestions including making the apphost in the test static, but nothing seems to work for me. I then tried the following test which also generated the same error which suggests to me that there is something in the apphost which is wrong but I can't see what.
[TestFixture(Category = "AppHost")]
public class AppHostTests
{
/// <summary>
/// The app host doesnt throw exception.
/// </summary>
[Test]
public void AppHostDoesntThrowException()
{
var apphost = new AppHost();
Assert.That(() => apphost.Init(),
Throws.Nothing);
}
}
The tests that generate this error whether I am using NCRUNCH (set to run one at a time) or if I use resharpers run all tests. It's generally the same tests that generate this error, though that seems to vary. In all cases, if I then run the tests manually they all pass.
You can only have 1 AppHost initialized and running at the same time where somehow NCrunch test is being run whilst there is another AppHost still in use. Maybe you can try debugging and setting a breakpoint that checks if ServiceStackHost.Instance is not null before trying to initialize another AppHost.
Note the AppHostBase is an ASP.NET Web App which may be causing the interference if it's running in the same project as the unit tests. If you want an integration test use AppSelfHostBase instead which you would use in place of BasicAppHost where you'd want to run a full integration test.

How to resolve runtime dependencies in castle windsor for C#

I have a specific scenario here where I need to pass the connection string based on the user, because users may be mapped to the different databases based on his/her enterprise.
This is the code I use to resolve the dependency with a static variable:
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(
Component.For<IUserRepository>()
.ImplementedBy(typeof(IKS.Dare.Optimix.Repository.EntityFramework.UserModule.UserRepository))
.DependsOn(Dependency.OnValue("connectionString", DatabaseSettings.DefaultConnectionString))
);
}
Because this DefaultConnectionString is supposed to be a dynamic one, I don't want to lock this variable to make it thread safe, as this would degrade the performance. I would want a way so that I can deal with such situation.
Possible consideration which can be that we can give a session, which can be applied as follows:
DynamicParameters((k, d) => d["connectionString"] = Session["connectionString"])
But this is in a different project which doesn't utilize any web component, it's just an installer project which is basically designed for resolving the dependencies only.
My Generic repository looks like following
public class GenericRepository<T> : IGenericRepository<T> where T : BaseEntity
{
private const string IsActive = "IsActive", DbContext = "dbContext", EntityPropertyName = "Entity";
private string connectionString = String.Empty, provider = String.Empty;
public GenericRepository(string connectionString, string provider)
{
this.connectionString = connectionString;
this.provider = provider;
}
public int Count()
{
string tableName = typeof(T).Name;
string query = SqlQueryConstants.SelectCount + SqlQueryConstants.Space + tableName;
int count = DbHelper.ExecuteScalar<int>(query: query, commandType: System.Data.CommandType.Text, connectionString: connectionString, provider: provider, parameters: null);
return count;
}
}
DBHelper class looks like follows
public static int ExecuteNonQuery(string query, CommandType commandType = CommandType.StoredProcedure,
IList<DbParameter> parameters = null, int? timeout = null, string connectionString = "", string provider = "")
{
using (var connection = CreateDbConnection(connectionString, provider))
{
connection.Open();
using (DbCommand command = CreateDbCommand(sqlQuery: query, parameters: parameters,
connection: connection, commandType: commandType, timeout: timeout))
{
return command.ExecuteNonQuery();
}
}
}
public static DbParameter CreateParameter<TValue>(string name, TValue value, DbType dbType,
ParameterDirection parameterDirection = ParameterDirection.Input, string provider = "")
{
DbParameter param = CreateDbProviderFactory(provider).CreateParameter();
param.Value = value;
param.ParameterName = name;
param.DbType = dbType;
param.Direction = parameterDirection;
return param;
}
public static DbConnection CreateDbConnection()
{
return CreateDbConnection(String.Empty, String.Empty);
}
public static DbConnection CreateDbConnection(string connectionString = "", string provider = "")
{
DbConnection connection = null;
if (String.IsNullOrEmpty(provider))
{
if (String.IsNullOrEmpty(DatabaseSettings.DefaultProvider))
throw new ArgumentNullException("provider");
else
provider = DatabaseSettings.DefaultProvider;
}
connection = CreateDbProviderFactory(provider).CreateConnection();
connection.ConnectionString = connectionString;
return connection;
}
Any help would be greatly appreciated.
Note : I couldn't edit steven's answer.
[EDIT] To make it more clear it can be implemented as:
Here controller is inherited from BaseController
public class UserController : BaseController
{
//
// GET: /Index/
private IUserRepository userRepository;
public UserController(IUserRepository userRepository)
: base(userRepository)
{
this.userRepository = userRepository;
}
}
and BaseController is inherited from Controller where in the database settings are being set in the constructor of Base controller so that we don't need to set it everywhere
public abstract class BaseController : Controller
{
public BaseController(IUserRepository userRepository)
{
userRepository.connectionStringProvider.Provider = WebUtilities.CurrentUserData.Provider;
userRepository.connectionStringProvider.ConnectionString = WebUtilities.CurrentUserData.ConnectionString;
}
}
Since, the connection string is runtime data, you should not use it to construct your application components, as is described in this article. So as the article advices, you should hide the connection string behind a provider abstraction. For instance:
public interface IConnectionStringProvider {
string ConnectionString { get; }
}
This way your repositories can depend on IConnectionStringProvider and can call IConnectionStringProvider.ConnectionString at runtime:
public int Count()
{
string tableName = typeof(T).Name;
string query = SqlQueryConstants.SelectCount + SqlQueryConstants.Space + tableName;
return DbHelper.ExecuteScalar<int>(
this.connectionStringProvider.ConnectionString,
provider: provider, parameters: null);
}
It will be trivial to create an IConnectionStringProvider to will get the correct connection string for you:
class DatabaseConnectionStringProvider : IConnectionStringProvider
{
public string ConnectionString => Session["connectionString"];
}
Since this clas depends on application-specifics (the ASP.NET session in this case), the class should not be part of the application's core logic. Instead, this adapter should live in the application's start up path (a.k.a. the composition root, the place where you configure your container).
You might even want to consider not passing along the IConnectionStringProvider into your repositories, but instead create an abstraction that will create a connection itself. This will hide the fact that there is a connection string completely.
What you're looking for is multi tenancy. You can google "castle windsor multi tenancy" and find a number of useful articles.
Here's a similar Stackoverflow question that links to some good articles on Windsor and multi tenancy. In particular, look into Windsor's IHandlerSelector interface.

Remove/Delete all/one item from StackExchange.Redis cache

I am using StackExchange.Redis client with Azure Redis Cache Service. Here is my class,
public class RedisCacheService : ICacheService
{
private readonly ISettings _settings;
private readonly IDatabase _cache;
public RedisCacheService(ISettings settings)
{
_settings = settings;
var connectionMultiplexer = ConnectionMultiplexer.Connect(settings.RedisConnection);
_cache = connectionMultiplexer.GetDatabase();
}
public bool Exists(string key)
{
return _cache.KeyExists(key);
}
public void Save(string key, string value)
{
var ts = TimeSpan.FromMinutes(_settings.CacheTimeout);
_cache.StringSet(key, value, ts);
}
public string Get(string key)
{
return _cache.StringGet(key);
}
public void Remove(string key)
{
// How to remove one
}
public void Clear()
{
// How to remove all
}
}
Update: From the help of Marc, Here is my final class
public class RedisCacheService : ICacheService
{
private readonly ISettings _settings;
private readonly IDatabase _cache;
private static ConnectionMultiplexer _connectionMultiplexer;
static RedisCacheService()
{
var connection = ConfigurationManager.AppSettings["RedisConnection"];
_connectionMultiplexer = ConnectionMultiplexer.Connect(connection);
}
public RedisCacheService(ISettings settings)
{
_settings = settings;
_cache = _connectionMultiplexer.GetDatabase();
}
public bool Exists(string key)
{
return _cache.KeyExists(key);
}
public void Save(string key, string value)
{
var ts = TimeSpan.FromMinutes(_settings.CacheTimeout);
_cache.StringSet(key, value, ts);
}
public string Get(string key)
{
return _cache.StringGet(key);
}
public void Remove(string key)
{
_cache.KeyDelete(key);
}
public void Clear()
{
var endpoints = _connectionMultiplexer.GetEndPoints(true);
foreach (var endpoint in endpoints)
{
var server = _connectionMultiplexer.GetServer(endpoint);
server.FlushAllDatabases();
}
}
}
Now I don't know how to remove all items or single item from redis cache.
To remove a single item:
_cache.KeyDelete(key);
To remove all involves the FLUSHDB or FLUSHALL redis command; both are available in StackExchange.Redis; but, for reasons discussed here, they are not on the IDatabase API (because: they affect servers, not logical databases).
As per the "So how do I use them?" on that page:
server.FlushDatabase(); // to wipe a single database, 0 by default
server.FlushAllDatabases(); // to wipe all databases
(quite possibly after using GetEndpoints() on the multiplexer)
I could not able to flush database in Azure Redis Cache, got this error:
This operation is not available unless admin mode is enabled: FLUSHDB
Instead iterate through all keys to delete:
var endpoints = connectionMultiplexer.GetEndPoints();
var server = connectionMultiplexer.GetServer(endpoints.First());
//FlushDatabase didn't work for me: got error admin mode not enabled error
//server.FlushDatabase();
var keys = server.Keys();
foreach (var key in keys)
{
Console.WriteLine("Removing Key {0} from cache", key.ToString());
_cache.KeyDelete(key);
}
Both answers by #Rasi and #Marc Gravell contain pieces of code needed.
Based on above, here is working snippet assuming there is just 1 server:
You need to connect to redis with allowAdmin=true, one way to obtain such options is to assign AllowAdmin to already parsed string:
var options = ConfigurationOptions.Parse("server:6379");
options.AllowAdmin = true;
var redis = ConnectionMultiplexer.Connect(options);
Then to flush all databases:
var endpoints = redis.GetEndPoints();
var server = redis.GetServer(endpoints[0]);
server.FlushAllDatabases();
Above will work on any redis deployment, not just Azure.
You can delete hash as well ie if you want to clear specific value from any cached list.
For example, we have an emp list and inside with different department as cached.
public static void DeleteHash(string key, string cacheSubKey)
{
if (string.IsNullOrEmpty(key))
throw new ArgumentNullException("key");
Cache.HashDelete(key, cacheSubKey);
}
so you can pass Key name and cache subkey as well.

How to set log4net's log file location in autofac?

I am now adding controller log by following this post: Using Autofac to inject log4net into controller
After doing that, I can get my application run correctly. Below are the details:
LogInjectionModule here:
public class LogInjectionModule:Module
{
protected override void AttachToComponentRegistration(Autofac.Core.IComponentRegistry componentRegistry, Autofac.Core.IComponentRegistration registration)
{
registration.Preparing += OnComponentPreparing;
}
static void OnComponentPreparing(object sender, PreparingEventArgs e)
{
var t = e.Component.Activator.LimitType;
e.Parameters = e.Parameters.Union(new[]
{
new ResolvedParameter((p, i) => p.ParameterType == typeof(ILog), (p, i) => LogManager.GetLogger(t))
});
}
}
DependencyRegister here:
private void RegisterDependency()
{
var builder = new ContainerBuilder();
builder.RegisterControllers(Assembly.GetExecutingAssembly());
builder.RegisterGeneric(typeof(Repository<>)).As(typeof(IRepository<>)).InstancePerHttpRequest();
builder.RegisterType<UnitOfWork>().As<IUnitOfWork>().InstancePerHttpRequest();
builder.RegisterType<BookContext>().As<IDbContext>().SingleInstance().PreserveExistingDefaults();
builder.RegisterType<ManagerRepository>().As<IManager>().InstancePerHttpRequest();
builder.RegisterType<BookLendRepository>().As<IBookLend>().InstancePerHttpRequest();
builder.RegisterType<BookPlaceRepository>().As<IBookPlace>().InstancePerHttpRequest();
builder.RegisterType<BookRepository>().As<IBook>().InstancePerHttpRequest();
builder.RegisterType<BookTypeRepository>().As<IBookType>().InstancePerHttpRequest();
builder.RegisterType<StudentRepository>().As<IStudent>().InstancePerHttpRequest();
builder.RegisterType<ManagerService>().As<IManagerService>().InstancePerHttpRequest();
builder.RegisterModule(new LogInjectionModule());
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}
MyController here:
public HomeController(
IManagerService managerService
,ILog logger
)
{
this.managerService = managerService;
this.logger = logger;
}
private readonly IManagerService managerService;
private readonly ILog logger;
public ActionResult Index(Manager manager)
{
logger.Info("test");
return View();
}
And when I debug to logger.Info("test") , I can get the log instance. But the problem is , where is the log file's location? is there any config for the integrated log4net that I can decide where to put the log file?
The question isn't related to Autofac. Log4net is a standalone library. You can read more about log4net on its homepage and how to configure it here.

How to write an NLog target using Signalr

I'm trying to write a target for NLog to send messages out to connected clients using SignalR.
Here's what I have now. What I'm wondering is should I be using resolving the ConnectionManager like this -or- somehow obtain a reference to the hub (SignalrTargetHub) and call a SendMessage method on it?
Are there performance ramifications for either?
[Target("Signalr")]
public class SignalrTarget:TargetWithLayout
{
public SignalR.IConnectionManager ConnectionManager { get; set; }
public SignalrTarget()
{
ConnectionManager = AspNetHost.DependencyResolver.Resolve<IConnectionManager>();
}
protected override void Write(NLog.LogEventInfo logEvent)
{
dynamic clients = GetClients();
var logEventObject = new
{
Message = this.Layout.Render(logEvent),
Level = logEvent.Level.Name,
TimeStamp = logEvent.TimeStamp.ToString("yyyy-MM-dd HH:mm:ss.fff")
};
clients.onLoggedEvent(logEventObject);
}
private dynamic GetClients()
{
return ConnectionManager.GetClients<SignalrTargetHub>();
}
}
I ended up with the basic the same basic structure that I started with. Just a few tweaks to get the information I needed.
Added exception details.
Html encoded the final message.
[Target("Signalr")]
public class SignalrTarget:TargetWithLayout
{
protected override void Write(NLog.LogEventInfo logEvent)
{
var sb = new System.Text.StringBuilder();
sb.Append(this.Layout.Render(logEvent));
if (logEvent.Exception != null)
sb.AppendLine().Append(logEvent.Exception.ToString());
var message = HttpUtility.HtmlEncode(sb.ToString());
var logEventObject = new
{
Message = message,
Logger = logEvent.LoggerName,
Level = logEvent.Level.Name,
TimeStamp = logEvent.TimeStamp.ToString("HH:mm:ss.fff")
};
GetClients().onLoggedEvent(logEventObject);
}
private dynamic GetClients()
{
return AspNetHost.DependencyResolver.Resolve<IConnectionManager>().GetClients<SignalrTargetHub>();
}
}
In my simple testing it's working well. Still remains to be seen if this adds any significant load when under stress.

Resources