Register and Retrieving Multiple Connection String using Autofac on .net core - autofac

When I register 2 connection string on autofac, i only retrieve the last registered connection string using keyfilter on IDbConnection.
//Register
builder.RegisterType<OracleConnection>()
.As(IDbConnection)....
.Named<IDbConnection>("conn1")
//Retrieve
Public Repository([KeyFilter]IDbConnection connection)....

Upon reading this topic:
http://autofac.readthedocs.io/en/latest/advanced/metadata.html
I just missed the .WithAttributeFiltering() when registering my component with the filter so the container knows to look for it.

Related

Quarkus: "no tenant identifier specified" in callback

I try to add multi-tenancy support for my Quarkus app, following Quarkus hibernate-orm doc (see last section).
I have my CustomTenantResolver class and configure in application.properties, with multiple data sources, but no named persistent unit, see below:
# Default data source
quarkus.hibernate-orm.datasource=master
quarkus.hibernate-orm.database.generation=none
quarkus.hibernate-orm.multitenant=DATABASE
# ----- Tenant 'master' (default) ---------------
quarkus.datasource."master".db-kind=postgresql
quarkus.datasource."master".username=postgres
quarkus.datasource."master".password=password
quarkus.datasource."master".jdbc.url=jdbc:postgresql://localhost:5432/db_master
# ----- Tenant 'test' ---------------------------
quarkus.datasource.test.db-kind=postgresql
quarkus.datasource.test.username=postgres
quarkus.datasource.test.password=password
quarkus.datasource.test.jdbc.url=jdbc:postgresql://localhost:5432/db_test
Everything works fine for Web Services APIs functions - based on incoming web service calls, I can extract and supply tenant identifier for DB access.
Problem is, my app also needs to use callback method to listen on messages coming from Apache Pulsar queue. When a message comes in and triggers this callback, any DB access in this method will give this exception:
SessionFactory configured for multi-tenancy, but no tenant identifier specified: org.hibernate.HibernateException: SessionFactory configured for multi-tenancy, but no tenant identifier specified
at org.hibernate.internal.AbstractSharedSessionContract.<init>(AbstractSharedSessionContract.java:172)
at org.hibernate.internal.AbstractSessionImpl.<init>(AbstractSessionImpl.java:29)
at org.hibernate.internal.SessionImpl.<init>(SessionImpl.java:221)
at org.hibernate.internal.SessionFactoryImpl$SessionBuilderImpl.openSession(SessionFactoryImpl.java:1282)
at org.hibernate.internal.SessionFactoryImpl.openSession(SessionFactoryImpl.java:472)
at io.quarkus.hibernate.orm.runtime.session.TransactionScopedSession.acquireSession(TransactionScopedSession.java:86)
at io.quarkus.hibernate.orm.runtime.session.TransactionScopedSession.persist(TransactionScopedSession.java:138)
at io.quarkus.hibernate.orm.runtime.session.ForwardingSession.persist(ForwardingSession.java:53)
... (snipped)
Apparently my CustomTenantResolver class was not called during this listener callback as the callback is another fresh thread, hence no tenant id is supplied.
Do I miss anything? How about the scheduler in Quarkus - how does it support multi-tenancy in scheduled jobs?
Thanks for helps.
I had a similar issue when pulling messages from JMS. The cause of the issue is that io.quarkus.hibernate.orm.runtime.tenant.HibernateCurrentTenantIdentifierResolver ( which implements CurrentTenantIdentifierResolver and as the doc says Maps from the Quarkus {#link TenantResolver} to the Hibernate {#link CurrentTenantIdentifierResolver} model ) expects a request context to be active before calling our implementation of TenantResolver, as shown here:
// Make sure that we're in a request
if (!Arc.container().requestContext().isActive()) {
return null;
}
TenantResolver resolver = tenantResolver(persistenceUnitName);
String tenantId = resolver.resolveTenantId();
I solved it on my app by, first, enabling the request context on the JMS consumer:
Arc.container().requestContext().activate();
and, second, using a ThreadLocal to "pass" the current tenant id to the TenantResolver that will be called later by Hibernate ( through the HibernateCurrentTenantIdentifierResolver instance):
CurrentTenantLocal.setCurrentTenantId("public");
On my TenantResolver ( the class that implements TenantResolver ) I resolve the tenant from either an injected JsonWebToken jwt when it comes from a WebRequest, or using the ThreadLocal when consuming from JMS:
if ( CurrentTenantLocal.getCurrentTenantId() != null ) {
return CurrentTenantLocal.getCurrentTenantId();
}
Caveats:
Note that I haven't done an exhaustive search of the possible side effects of activating the request context... but I have no problems so far.

Persistent connection string errors in .NET Core DB2 connector

I am migrating a full framework application to .NET Core. Under the full framework, it used the following connection string with the IBM .NET Connector for DB2:
"Server=localhost:50000;Database=testdb;"
The code then assigned UserID and Password properties from credentials vault.
Now, under Core, with the IBM .NET Core connector for DB2 specifically v.2.0.0.100 (long-term support, according to IBM), this connection string throws an exception when a connection string builder is created from it:
{System.ArgumentNullException: Value cannot be null.
at System.Threading.Monitor.ReliableEnter(Object obj, Boolean& lockTaken)
at IBM.Data.DB2.Core.DB2ConnPool.ReplaceConnStrPwd(String value, String newvalue, Boolean onlyPwd)
at IBM.Data.DB2.Core.DB2Connection.RemoveConnectionStringPassword(String value, Boolean bMask)
at IBM.Data.DB2.Core.DB2ConnectionStringBuilder..ctor(String connectionString)
There is no InnerException. I presume that some mandatory parameters of the connection string that I am not aware of have to be populated under Core, whereas under full framework they were optional. A careful read of IBM documents on DB2 connector Core yielded no mentions of connection string changes, unless I missed them. This blog post mentioned no such breaking changes.
Is anyone aware of mandatory connection string parameters that are missing from my connection string specifically for .NET Core connector?
UPDATE:
If I build the connection string manually, by concatenating the original one above with UserID=MyUser;Password=MyPWD;, and open a connection to the database, then DB2ConnectionStringBuilder works even with the original one above. I cannot wrap my head around it! This makes zero sense. The whole purpose of a connection string builder is to build connection strings from parameters, in a strongly-typed manner. Any ideas?
If you use & IBM.Data.DB2.Core (3.1) nuget package with a connection like:
server=my server;database=myDatabase;user id=myUser;password=MyPassword
Then OdbcConnectionStringBuilder will be enough (e.g.):
var db2Connection = "server=my server;database=myDatabase;user id=myUser;password=MyPassword";
var connectionBuilder = new OdbcConnectionStringBuilder(connection);
var Db2Connection = new Db2Connection(builder.ToString());
or something like this:
var password = builder["password"]?.ToString();
It works fine for me

spring cloud programmatic metadata generation

Is there anyway that I can generate some metadata to add to the service when it registers.
We are moving from Eureka to Consul and I need to add a UUID value to the registered metadata when a service starts. So that later I can get this metadata value when I retrieve the service instances by name.
Some background: We were using this excellent front end UI from https://github.com/VanRoy/spring-cloud-dashboard. It is set to use the Eureka model for services in which you have an Application with a name. Each application will have multiple instances each with an instance id.
So with the eureka model there is a 2 level service description whereas the spring cloud model is a flat one where n instances each of which have a service id.
The flat model won't work with the UI that I referenced above since there is no distinction between application name and instance id which is the spring model these are the same.
So if I generate my own instance id and handle it through metadata then I can preserve some of the behaviour without rewriting the ui.
See the documentation on metadata and tags in spring cloud consul. Consul doesn't support metadata on service discovery yet, but spring cloud has a metadata abstraction (just a map of strings). In consul tags created with key=value style are parsed into that metadata map.
For example in, application.yml:
spring:
cloud:
consul:
discovery:
tags: foo=bar, baz
The above configuration will result in a map with foo→bar and baz→baz.
Based on Spencer's answer I added an EnvironmentPostProcessor to my code.
It works and I am able to add the metadata tag I want programmatically but it is a complement to the "tags: foo=bar, baz" element so it overrides that one. I will probably figure a way around it in the next day or so but I thougth I would add what I did for other who look at this answer and say, so what did you do?
first add a class as follows:
#Slf4j
public class MetaDataEnvProcessor implements EnvironmentPostProcessor, Ordered {
// Before ConfigFileApplicationListener
private int order = ConfigFileApplicationListener.DEFAULT_ORDER - 1;
private UUID instanceId = UUID.randomUUID();
#Override
public int getOrder() {
return this.order;
}
#Override
public void postProcessEnvironment(ConfigurableEnvironment environment,
SpringApplication application) {
LinkedHashMap<String, Object> map = new LinkedHashMap<>();
map.put("spring.cloud.consul.discovery.tags", "instanceId="+instanceId.toString());
MapPropertySource propertySource = new MapPropertySource("springCloudConsulTags", map);
environment.getPropertySources().addLast(propertySource);
}
}
then add a spring.factories in resources/META-INF with eht following line to add this processor
org.springframework.boot.env.EnvironmentPostProcessor=com.example.consul.MetaDataEnvProcessor
This works fine except for the override of what is in your application.yml file for tags

Keycloak Custom Validation Output messages

I'm using jboss keycloak 1.5 final version.
I developed my custom user federation provider interfacing with keycloak properties and my user enterprise database.
My need is to send up to user the login interface custom error messages based on particular specific error related to my legacy user db.
I saw keycloak themes have a resources folder by which i can localize and add new messages. Then i can reference them by angular js using
$myMessage
notation. The problem is i want to rise up a message from keycloak server. My user federation provider implements UserFederationProvider interface. So i should have to override:
#Override
public CredentialValidationOutput validCredentials(RealmModel realm, UserCredentialModel credential) {
LOGGER.info("validCredentials(realm, credential)");
return CredentialValidationOutput.failed();
}
which seems to be the method i was looking for just because CredentialValidationOutput contains custom messages to be sent as validation output. The problem is this method is never called.
Why?
I'll post the answer found on my own.
It's necessary to develop your own Authenticator. For example refer to Keycloak UsernameAndForm and UsernameAndFormFactory implementation.
You can find them on Keycloak github source code:
https://github.com/keycloak/keycloak/tree/master/services/src/main/java/org/keycloak/authentication/authenticators/browser
The main validation method are:
public boolean validateUserAndPassword(AuthenticationFlowContext context, MultivaluedMap<String, String> inputData) {
...
}
public boolean validatePassword(AuthenticationFlowContext context, UserModel user, MultivaluedMap<String, String> inputData) {
...
}
From your custom user federation provider you can throw your custom exception and catch them in the two methods above adding:
catch (YourCustomException ex){
...
Response challengeResponse = context.form()
.setError("YOUR ERROR MESSAGE", me.getMandator()).createLogin();
context.failureChallenge(AuthenticationFlowError.INVALID_USER, challengeResponse);
return false;
}
Of course in your project you have to add
META-INF/service/org.keycloak.authentication.AuthenticatorFactory
In which you specify the full qualified name of your AuthenticatorFactory.
For a valid guide make reference to Keycloak User Guide 1.6.1 Final. Chapter 33.3

Why connectionstring without metadata works?

I had connection strings for entity framework edmx, which is usual EF connection string with metadata.
Now i am implementing mvc-mini-profiler and wrote method below to create context. I am using just sql connection string part now, no longer using EF connection string.
Now it works but i am curious how it is getting metadata(.csdl, .ssdl address), If it can find now then why 'new Context()' need metadata
public static T GetProfiledContext<T>() where T : ObjectContext
{
// create connection
var sqlConnection = new SqlConnection(ConfigurationManager.ConnectionStrings["LocalSqlServer"].ConnectionString);
// wrap the connection with a profiling connection that tracks timings
var profiledDbConnection = MvcMiniProfiler.Data.ProfiledDbConnection.Get(sqlConnection, MiniProfiler.Current);
// create context
return profiledDbConnection.CreateObjectContext<T>();
}
The reason why it works without metadata is that CreateObjectContext extension method will add these metadata when creating the context. It uses wildcards: res://*/ to get metadata. You can check the implementation here.