Dynamic class loading and application context creation Spring application migration to Quarkus - spring-boot

I'm new to Quarkus and I'm analysing the migration viability of a peculiar "Spring Boot" application.
The application is a "rules engine" used by the company's own CRM in order to provide a Wizard to sales and quoting processes; making all the business, elegibility and product structure complexity transparent to the CRM application.
To accomplish that, the CORE of the application counts with several "rules bases" which can configured/programed based on Java, Drools and other languages/definitions.
When this application starts, it detects all previously compiled/stored bases and deserializes them.
But, if a base is not found among the serialized ones, it compiles and stores it to avoid this high consuming step next time.
The application allows also to reprocess a specific base "on the fly" (at the end of the reprocessing fase, the current version of the "rules base" is replaced by the one that was just compiled).
The problem I'm facing is regarding the dynamic loading of the bases which involves, among other this, the dynamic creation of application context and class loading like on code snippet below:
public AnnotationConfigApplicationContext processPackages(
Set< String > packages,
ClassLoader loader,
String configName,
boolean doRefresh ) {
if ( applicationContext != null && packages != null ) {
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();
annotationConfigApplicationContext.setParent( applicationContext );
annotationConfigApplicationContext.setClassLoader( loader );
RulesEngineContext context = new RulesEngineContext( configName, null );
RulesEngineContextHolder.set( context );
packages.add( GLOBAL_BEAN_CONFIGURATOR );
for ( String pkg : packages ) {
annotationConfigApplicationContext.scan( pkg );
}
if ( doRefresh ) {
annotationConfigApplicationContext.refresh();
}
return annotationConfigApplicationContext;
}
return null;
}
The only possibility I could imagine until now to migrate this application to Quarkus will demand a considerable refactoring with the loss of "on the fly" reprocessing of "rules bases" and the creation of a Quarkus extension to load and index all dependencies demanded by all "rules bases" before startup.
Does anyone could help me out with any ideas how to accomplish the migration of such application?
Thanks and best regards!

Related

singleton at the boot of application

I'm creating a small game where I would like to have a game room and a bunch of games in the game room. This would be a spring boot application. So I was thinking of starting the GameRoom at the start of the application. Then whenever a new game is created, I will add it to the list of games in the game roon. Is is a good idea? Or are there any potential pitfalls.
public class GameRoon{
private static GameRoom instance = null;
List<Game> games = new ArrayList();
private GameRoom() {}
public static GameRoom getIsntance() {
if(instance == null) {
instance = new GameRoom();
}
return instance;
}
}
The above is my unfinished singleton.. I have 2 questions.
1. Is it a good idea to start this during spring-boot start up?
2. Is it better to use singleton like this in spring-boot or is there a better way to do it
Singletons are highly debated and you will always find reasons for using it and for not.
In my opinion if your class responsibility is to hold a state (think of a registry with some data) which is shared within the application I don't see any problem. It is clear that you share the data and must be sure to govern concurrent access.
The main drawback of Singleton is testing: you have a global state but ideally during unit testing you want to test some code without relying on the singleton class (instead a mock). You can still solve this thanks to Spring: define the GameRoom as Spring bean (default scope is Singleton) which is injected like any other bean. This allows to mock it during unit testing, even if underneath there is a singleton behavior.

Can we use UmlStateMachineModelFactory inside a StateMachineBuilder

I am using StateMachineBuilder to create state machine , coz I needed to use it programatically to configure states and transitions.
But due to some requirement change my configuration is almost constant and now I wanted to use eclipse uml modelling since I no longer need to build state machine dynamically or programatically.
To avoid big code rework I thought of using UmlStateMachineModelFactory inside builder like below.
Builder<String, String> builder = StateMachineBuilder
.<String, String> builder();
builder.configureConfiguration()
.withConfiguration()
.autoStartup(false)
.listener(listener())
.beanFactory(
this.applicationContext.getAutowireCapableBeanFactory());
builder.configureModel().withModel().factory(new UmlStateMachineModelFactory("classpath:model2.uml"));
1) Is it legal in state machine, if so how can I attach entry actions for each state?
Currently using the builder I am using the below code for attaching entry actions for each state
stateConfigurer.state("State1", this.state1EntryAction, null);
// State1EntryAction is #Autowired inside my controller class and
// State1EntryAction implements Action<String, String> and
// annotated with #Component (org.springframework.stereotype.Component)
2) Can i give the name of entry action class inside the uml model, order to attach entry actions for each stage? if so how can i do that in eclipse papyrus.
Thanks in Advance!
Docs are probably a little unclear of this(especially if user is not familiar with papyrus and uml concepts). I'd start by studying uml test sources in uml xml test files how actions/guards are referenced from uml into a runtime resolving. Things become more clear when you see those using real papyrus uml modeler.
On default guard/action will resolve as equivalent bean name from an application context, but there is a way to hook additional instances manually(i.e. if guard/action is not defined as a bean).
This test testSimpleFlat2() will register additional actions/guards. These are resolved via StateMachineComponentResolver interface and internally instance of DefaultStateMachineComponentResolver will resolve from BeanFactory if it's known but user can add secondary DefaultStateMachineComponentResolver as demonstrated in that test.

How to create a dynamic property value in an OSGi DS service registration

Here's a bit of code from the Apache CXF documentation:
CustomMessageBodyReaderWriter provider1 = new CustomMessageBodyReaderWriter();
provider.setCustomProperty(true);
Dictionary properties = new Hashtable();
properties.put("org.apache.cxf.rs.provider", provider);
bundleContext.registerService(
new String[]{"org.books.BookService"}, new BookServiceImpl(), properties);
Note that this piece of an activator method registers an OSGi service where one of the property values is an object created and configured at runtime.
Now, what if I wanted this to be a CXF dOSGi component? The only way I know to specify service registration properties for DS #Components requires the property value to be a string in the 'properties' slot in the #Component. Is there some way to have executable code involved?
CXF is not using the service properties correctly. The spec says :
Properties hold information as key/value pairs. The key must be a
String object and the value should be a type recognized by Filter
objects (see Filters on page 138 for a list). Multiple values for the
same key are supported with arrays ([]) and Collection objects. The
values of properties should be limited to primitive or standard Java
types to prevent unwanted inter bundle dependencies. ...
The service properties are intended to provide information about the
service. The properties should not be used to participate in the
actual function of the service. Modifying the properties for the
service registration is a potentially expensive operation. For
example, a Framework may pre-process the properties into an index
during registration to speed up later look-ups.
Anyway, AFAIK, it's not possible with the current DS to create such properties. You can however :
The 'DS way', use an immediate component which creates the real component with a ComponentFactory
Use an immediate component, and register your service with the raw osgi API
If you use felix SCR, you can use a ExtComponentContext to override your component properties
Update:
an example of a ComponentFactory :
#Component(factory = "bookService")
public class BookServiceImpl implements BookService {
...
}
And a component using this factory :
#Component
public class BookServiceManager {
#Reference(target = "(component.factory=bookService)")
private ComponentFactory bookServiceFactory;
#Activate
public void start() {
CustomMessageBodyReaderWriter provider1 = new CustomMessageBodyReaderWriter();
provider.setCustomProperty(true);
Dictionary properties = new Hashtable();
properties.put("org.apache.cxf.rs.provider", provider);
bookServiceFactory.newInstance(properties);
}
}
To be honest, with this use-case, I prefer using the raw OSGi API. But this approach can be useful if you want DS to manage your #Reference in your ComponentFactory. When the dependencies are not satisfied, the ComponentFactory and all its ComponentInstance will be deactivated.
I am currently working on a 2.0 version of CXF-DOSGi which will allow to set provder instances as intents. You can then publish an instance of CustomMessageBodyReaderWriter under an intent name and refer to it from your remote service by listing the required intents.

Membership reboot replace Ninject with Simple Injector

I need add membership reboot (RavenDb) into the project that use IOC Simple Injector
Ninject implementation
var config = MembershipRebootConfig.Create();
kernel.Bind<MembershipRebootConfiguration<HierarchicalUserAccount>>().ToConstant(config);
kernel.Bind<UserAccountService<HierarchicalUserAccount>>().ToSelf(); kernel.Bind<AuthenticationService<HierarchicalUserAccount().To<SamAuthenticationService<HierarchicalUserAccount>>();
kernel.Bind<IUserAccountRepository<HierarchicalUserAccount>>().ToMethod(ctx => new BrockAllen.MembershipReboot.RavenDb.RavenUserAccountRepository("RavenDb"));
kernel.Bind<IUserAccountQuery>().ToMethod(ctx => new BrockAllen.MembershipReboot.RavenDb.RavenUserAccountRepository("RavenDb"));
Simple Injector implementation
container.Register(MembershipRebootConfig.Create);
container.Register<UserAccountService<HierarchicalUserAccount>>();
container.Register<AuthenticationService<HierarchicalUserAccount>, SamAuthenticationService<HierarchicalUserAccount>>();
container.Register<IUserAccountRepository<HierarchicalUserAccount>>(() => new RavenUserAccountRepository("RavenDb"), Lifestyle.Singleton);
container.Register<IUserAccountQuery>(() => new RavenUserAccountRepository("RavenDb"));
On row
container.Register<UserAccountService<HierarchicalUserAccount>>();
I have an error
For the container to be able to create UserAccountService, it should contain exactly one public constructor, but it has 2.
Parameter name: TConcrete
Thanks for your help.
Simple Injector forces you to let your components to have one single public constructor, because having multiple injection constructors is an anti-pattern.
In case the UserAccountService is part of your code base, you should remove the constructor that should not be used for auto-wiring.
In case the UserAccountService is part of a reusable library, you should prevent using your container's auto-wiring capabilities in that case as described here. In that case you should fallback to wiring the type yourself and let your code call into the proper constructor, for instance:
container.Register<UserAccountService<HierarchicalUserAccount>>(() =>
new UserAccountService<HierarchicalUserAccount>(
container.GetInstance<MembershipRebootConfiguration<HierarchicalUserAccount>>(),
container.GetInstance<IUserAccountRepository<HierarchicalUserAccount>>()));
I'm just going to include here how I converted the Ninject configuration to Simple Injector for the Single Tenant sample in the MembershipReboot repository (which I cloned). I thought that might be beneficial for anyone who was searching for how to go about this, as it may save them some time.
Firstly, the configuration in the Single Tenant sample's NinjectWebCommon class is:
var config = MembershipRebootConfig.Create();
kernel.Bind<MembershipRebootConfiguration>().ToConstant(config);
kernel.Bind<DefaultMembershipRebootDatabase>().ToSelf();
kernel.Bind<UserAccountService>().ToSelf();
kernel.Bind<AuthenticationService>().To<SamAuthenticationService>();
kernel.Bind<IUserAccountQuery>().To<DefaultUserAccountRepository>().InRequestScope();
kernel.Bind<IUserAccountRepository>().To<DefaultUserAccountRepository>().InRequestScope();
Now, I'll set out the whole SimpleInjectorInitializer class, which started with the one which was added to the project via the SimpleInjector.MVC3 Nuget package, and follow up with comments:
public static class SimpleInjectorInitializer
{
/// <summary>Initialize the container and register it as MVC3 Dependency Resolver.</summary>
public static void Initialize()
{
var container = new Container();
container.Options.DefaultScopedLifestyle = new WebRequestLifestyle();
container.RegisterMvcControllers(Assembly.GetExecutingAssembly());
InitializeContainer(container);
container.Verify();
DependencyResolver.SetResolver(new SimpleInjectorDependencyResolver(container));
}
private static void InitializeContainer(Container container)
{
Database.SetInitializer(new MigrateDatabaseToLatestVersion<DefaultMembershipRebootDatabase, BrockAllen.MembershipReboot.Ef.Migrations.Configuration>());
var config = MembershipRebootConfig.Create();
container.Register(() => config, Lifestyle.Singleton);
container.Register(() => new DefaultMembershipRebootDatabase(), Lifestyle.Scoped);
container.Register<IUserAccountQuery, DefaultUserAccountRepository>(Lifestyle.Scoped); // per request scope. See DefaultScopedLifestyle setting of container above.
container.Register<IUserAccountRepository, DefaultUserAccountRepository>(Lifestyle.Scoped);
container.Register(() => new UserAccountService(container.GetInstance<MembershipRebootConfiguration>(), container.GetInstance<IUserAccountRepository>()));
container.Register<AuthenticationService, SamAuthenticationService>();
var iUserAccountQueryRegistration = container.GetRegistration(typeof(IUserAccountQuery)).Registration;
var iUserAccountRepositoryRegistration = container.GetRegistration(typeof(IUserAccountRepository)).Registration;
iUserAccountQueryRegistration.SuppressDiagnosticWarning(DiagnosticType.TornLifestyle, "Intend for separate Objects");
iUserAccountRepositoryRegistration.SuppressDiagnosticWarning(DiagnosticType.TornLifestyle, "Intend for separate Objects");
}
}
Scoping the config to a Singleton with a factory func is pretty much the same as Ninject's ToConstant.
DefaultMembershipRebootDatabase is the obvious departure, but I honestly don't think it matters whether MR's DefaultMembershipRebootDatabase is scoped a transient or per web request. It calls SaveChanges every time an operation is performed e.g. Registering a user. It does not use larger, per request-bound tansactions. So, using the same DefaultMembershipRebootDatabase context later in the same request is no going to cause any weird MR issues.
HOWEVER, some thought will need to given to what happens if you want to create a Domain User during the same operation as you create a MR UserAccount. (A Domain User may contain more information beyond password stuff, like first and last names, DOB etc.). Tying an MR UserAccount to a Domain User (with additional user info such a name, address etc.) is a common use case. So what happens if the creation of the Domain User fails after creation of the MR UserAccount succeeded? I don't know. Perhaps as part of the rollback, you delete the MR user. But the registration email will already have been sent. So, these are the issues that you face here.
As you can see, in the Simple Tenant sample, Brock registers both IUserAccountRepository and IUserAccountQuery to DefaultUserAccountRepository. This is obviously by design and so we have to do that as well, if we want to use MR's UserAccountService and AuthenticationService. Thus, we need to suppress the Diagnostic warnings which would otherwise prevent the Container from Verifying.
Hope that all helps and by all means let me know if there are problems with my registrations.
Cheers

Using Ninject in a SOLID application architecture

I'm starting with MVC3 and want to use some flexible architecture, so I've read tens of blogs, a book (Pro ASP.NET MVC 3), read about SOLID principles and finally got to an application structure I like (or at least I think so, so far, because I haven't built anything on it yet):
In this structure:
Domain holds the POCO classes and defines the service interfaces
Services implements service interfaces and defines repositories interfaces
Data implements repositories interfaces
WebUI and Domain use Services
Services use Repositories
WebUI, Services and Data depend on Domain for POCO classes
The main reason for Domain using Services is to validate unique keys on the Validate methods of POCO (IValidatable) classes.
I'm starting to build a reference application with this structure but I have faced, so far, two problems:
I'm using a Data.Tests project with unit tests for the repositories, but haven't found a way to inject (using Ninject) a implementation of the service (in the constructor or otherwise) on the model, so the Validate method can call the CheckUniqueKey on the service.
I haven't found any reference about hooking up Ninject to a TEST project (lots of for the WebUI project).
What I'm trying to achive here is beeing able to switch from EF to something else like DAPPER, by just changing the DATA assembly.
UPDATE
Right now (as of 09-AUG-2011) Ninject is working but I think I'm missing something.
I have a CustomerRepository with two constructors:
public class CustomerRepository : BaseRepository<Customer>, ICustomerRepository
{
// The repository usually receives a DbContext
public CustomerRepository(RefAppContext context)
: base(context)
{
}
// If we don't receive a DbContext then we create the repository with a defaulte one
public CustomerRepository()
: base(RefApp.DbContext())
{
}
...
}
On the TestInitialize:
// These are for testing the Repository against a test database
[TestInitialize()]
public void TestInitialize()
{
// Context used for tests
this.context = new RefAppContext();
// This is just to make sure Ninject is working,
// would have used: repository = new CustomerRepository(context);
this.kernel = NinjectMVC3.CreateKernel();
this.kernel.Rebind<ICustomerRepository>().To<CustomerRepository>().WithConstructorArgument("context", context);
this.repository = kernel.Get<ICustomerRepository>();
}
On the Customer class:
public class Customer : IValidatableObject
{
...
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
// I want to replace this with a "magic" call to ninject
CustomerRepository rep = new CustomerRepository();
Customer customer = rep.GetDupReferenceCustomer(this);
if (customer != null)
yield return new ValidationResult("Customer \"" + customer.Name + "\" has the same reference, can't duplicate", new [] { "Reference" });
}
...
}
What would be the best way to use Ninject in this scenario?
Any help will be highly appreciated.
ANSWER, SORT OF
I'll consider this question as aswered so far. I could get Ninject working, sort of, but it looks like achiving the Dependency Inversion Principle (DIP) of SOLID is going to take some more time.
In that respect, I had to lump together Domain, Services and Data, I'll create another question some other time and keep the project going the usual way for now.
Thanks everybody.
Unit testing should be done without Ninject. Just create an instance of the object under test and inject a mock for every dependency manually.
For Integration Tests you can use the kernel inclusive all bindings from the application bootstrapper and rebind everything you want to replace by a Mock. e.g. Replace the Session binding by one that uses an in memory data storage instead of a real database.

Resources