Jenkins Shared Library Immutable Singleton - jenkins

I have a Singleton patter class in my Jenkins shared library:
public class Configuration {
private static final INSTANCE = new Configuration()
static getInstance() { return INSTANCE }
private Configuration() {
}
def initialize(env, params) {
Foo = params.FOO;
}
public String Foo = ''
}
Later I can call this from elsewhere using something like this:
Configuration.instance.initialize(env, params);
config = Configuration.instance;
println 'FOO: ' + config.Foo
Ideally, I want the benefit of the Singleton pattern, but I don't want some fields to be overridden by consumers.
First Attempt:
On first thought, I would think this would work:
public class Configuration {
private static final INSTANCE = new Configuration()
static getInstance() { return INSTANCE }
private Configuration() {
}
def initialize(env, params) {
INSTANCE.#Foo = params.FOO;
}
public final String Foo = ''
}
Error:
groovy.lang.GroovyRuntimeException: Cannot set the property 'Foo' because the backing field is final.
Second Attempt:
On Second thought, I would think initializing in the constructor would work, however I don't seem to have access to params and env, unless these are passed in from the vars function, via the initialize() method.
How can I make this Singleton class immutable, or its fields read only?

I think you Could:
Define your class with "implements Serializable", as documentation advices.
Implement the constructor that would accept 1 parameter of type BaseScript, and pass this to it upon instantiation, relative to that this (which you could call internal script) you can refer to script.params, script.env, etc. and I mean you don't HAVE to use initialize, you can do all you want in the c'tor.
But wait, please tell more:
why does CI/CD code need to have a Singleton?
You're passing its data as parameters [so it's not really an immutable entity :)]
Maybe you could "simply" create an immutable map out of your parameters....
Configuration as singleton feels as if you can delegate configuration management to ... configuration management service (consul, etcd, or others).
Please elaborate, it's very curious!
Also you referred to something as "consumers". are these library consumers? or people running the jobs?
Thank you!

Related

How can I inject with Guice my api into dataflow jobs without needed to be serializable?

This question is a follow on after such a great answer Is there a way to upload jars for a dataflow job so we don't have to serialize everything?
This made me realize 'ok, what I want is injection with no serialization so that I can mock and test'.
Our current method requires our apis/mocks to be serialiable BUT THEN, I have to put static fields in the mock because it gets serialized and deserialized creating a new instance that dataflow uses.
My colleague pointed out that perhaps this needs to be a sink and that is treated differently? <- We may try that later and update but we are not sure right now.
My desire is from the top to replace the apis with mocks during testing. Does someone have an example for this?
Here is our bootstrap code that does not know if it is in production or inside a feature test. We test end to end results with no apache beam imports in our tests meaning we swap to any tech if we want to pivot and keep all our tests. Not only that, we catch way more integration bugs and can refactor without rewriting tests since the contracts we test are customer ones we can't easily change.
public class App {
private Pipeline pipeline;
private RosterFileTransform transform;
#Inject
public App(Pipeline pipeline, RosterFileTransform transform) {
this.pipeline = pipeline;
this.transform = transform;
}
public void start() {
pipeline.apply(transform);
pipeline.run();
}
}
Notice that everything we do is Guice Injection based so the Pipeline may be direct runner or not. I may need to modify this class to pass things through :( but anything that works for now would be great.
The function I am trying to get our api(and mock and impl to) with no serialization is thus
private class ValidRecordPublisher extends DoFn<Validated<PractitionerDataRecord>, String> {
#ProcessElement
public void processElement(#Element Validated<PractitionerDataRecord>element) {
microServiceApi.writeRecord(element.getValue);
}
}
I am not sure how to pass in microServiceApi in a way that avoid serialization. I would be ok with delayed creation as well after deserialization using guice Provider provider; with provider.get() if there is a solution there too.
Solved in such a way that mocks no longer need static or serialization anymore by one since glass bridging the world of dataflow(in prod and in test) like so
NOTE: There is additional magic-ness we have in our company that passes through headers from service to service and through dataflow and that is some of it in there which you can ignore(ie. the RouterRequest request = Current.request();). so for anyone else, they will have to pass in projectId into getInstance each time.
public abstract class DataflowClientFactory implements Serializable {
private static final Logger log = LoggerFactory.getLogger(DataflowClientFactory.class);
public static final String PROJECT_KEY = "projectKey";
private transient static Injector injector;
private transient static Module overrides;
private static int counter = 0;
public DataflowClientFactory() {
counter++;
log.info("creating again(usually due to deserialization). counter="+counter);
}
public static void injectOverrides(Module dfOverrides) {
overrides = dfOverrides;
}
private synchronized void initialize(String project) {
if(injector != null)
return;
/********************************************
* The hardest part is this piece since this is specific to each Dataflow
* so each project subclasses DataflowClientFactory
* This solution is the best ONLY in the fact of time crunch and it works
* decently for end to end testing without developers needing fancy
* wrappers around mocks anymore.
***/
Module module = loadProjectModule();
Module modules = Modules.combine(module, new OrderlyDataflowModule(project));
if(overrides != null) {
modules = Modules.override(modules).with(overrides);
}
injector = Guice.createInjector(modules);
}
protected abstract Module loadProjectModule();
public <T> T getInstance(Class<T> clazz) {
if(!Current.isContextSet()) {
throw new IllegalStateException("Someone on the stack is extending DoFn instead of OrderlyDoFn so you need to fix that first");
}
RouterRequest request = Current.request();
String project = (String)request.requestState.get(PROJECT_KEY);
initialize(project);
return injector.getInstance(clazz);
}
}
I suppose this may not be what you're looking for, but your use case makes me think of using factory objects. They may depend on the pipeline options that you pass (i.e. your PipelineOptions object), or on some other configuration object.
Perhaps something like this:
class MicroserviceApiClientFactory implements Serializable {
MicroserviceApiClientFactory(PipelineOptions options) {
this.options = options;
}
public static MicroserviceApiClient getClient() {
MyPipelineOptions specialOpts = options.as(MySpecialOptions.class);
if (specialOpts.getMockMicroserviceApi()) {
return new MockedMicroserviceApiClient(...); // Or whatever
} else {
return new MicroserviceApiClient(specialOpts.getMicroserviceEndpoint()); // Or whatever parameters it needs
}
}
}
And for your DoFns and any other execution-time objects that need it, you would pass the factory:
private class ValidRecordPublisher extends DoFn<Validated<PractitionerDataRecord>, String> {
ValidRecordPublisher(MicroserviceApiClientFactory msFactory) {
this.msFactory = msFactory;
}
#ProcessElement
public void processElement(#Element Validated<PractitionerDataRecord>element) {
if (microServiceapi == null) microServiceApi = msFactory.getClient();
microServiceApi.writeRecord(element.getValue);
}
}
This should allow you to encapsulate the mocking functionality into a single class that lazily creates your mock or your client at pipeline execution time.
Let me know if this matches what you want somewhat, or if we should try to iterate further.
I have no experience with Guice, so I don't know if Guice configurations can easily pass the boundary between pipeline construction and pipeline execution (serialization / submittin JARs / etc).
Should this be a sink? Maybe, if you have an external service, and you're writing to it, you can write a PTransform that takes care of it - but the question of how you inject various dependencies will remain.

Guice: object creation of unreferenced object

I have a class which should be created during the injection phase but this instance will not be referenced anywhere in the code. The way this class communicates with the others is via event bus
public class DefaultCounterTracker {
private final EventBus eventBus;
private final ReplicatedMap<String, String> trackerCache;
#Inject
public DefaultCounterTracker(
EventBus eventBus,
#Named("CountersTrackerCache") ReplicatedMap<String, String> trackerCache)
{
this.eventBus = eventBus;
this.trackerCache = trackerCache;
bindListeners();
}
private void bindListeners()
{
eventBus.localConsumer(CounterCreated.name(), (Handler<Message<String>>) event ->
{
handleCreation(event.body());
});
eventBus.localConsumer(CounterDestroyed.name(), (Handler<Message<String>>) event ->
{
handleDestruction(event.body());
});
}
Debugging I have seen that this class is created only when somewhere in the code there is written
#Inject
DefaultCounterTracker counterTracker
I have tried binding using a provider but nothing changes: it seems that if the class is not referenced Guice won't create it. Is there a way to tell Guice to do it? Did I miss the point?
I could also create the instance manually but if I do change in default implementation of one of the parameters (EventBus for instance) I need to remember also to change the call in the constructor.
Any suggestion appreciated
If the mentioned class is supposed to be a Singleton, then you can create an object of this class eagerly by specifying a binding in your configure() method of the AbstractModule class.
bind(DefaultCounterTracker.class).to(DefaultCounterTracker.class).asEagerSingleton();

Instance method with Guice

I would like to have a static instance method with Guice for one of the components (non-managed bean should be able to access this class). I created something like this:
public class LookupService {
#Inject
private static Provider<Injector> injector = null;
private final ILookup<IWS> lookup;
#Inject
public LookupService(ILookup<IWS> lookup) {
this.lookup = lookup;
}
public static LookupService instance() {
return injector.get().getInstance(LookupService.class);
}
public <T extends IWS> T lookup(Class<T> localInterface) {
return lookup.lookup(localInterface);
}
}
What do you think about this design ? Any other ideas on this ? (accessing managed beans from non-managed objects)
Basically, the pattern you're looking for is called "requesting static injection" and there's a Binder method dedicated to it. Once you have that down, your code looks a lot like this example from the Guice docs.
public class MainModule extends AbstractModule {
#Override public void configure() {
requestStaticInjection(LookupService.class);
}
}
public class LookupService {
/** This will be set as soon as the injector is created. */
#Inject
static Provider<LookupService> provider = null;
private final ILookup<IWS> lookup;
#Inject
public LookupService(ILookup<IWS> lookup) {
this.lookup = lookup;
}
public static LookupService instance() {
return provider.get();
}
public <T extends IWS> T lookup(Class<T> localInterface) {
return lookup.lookup(localInterface);
}
}
A few notes:
While you can still set your field to be private, remember that this means you cannot set it in tests (or in future non-Guice usage) without Guice's private-field-access magic. When using injected fields, we often make them package-private and then put the tests in the same package.
Static injection is generally seen as something to endorse only when migrating to Guice, or when you use other code you can't change. When possible, try to avoid global state--even if this means making FooBean data-only and creating an injected FooBeanService.
Even though you can inject an Injector wherever you'd like, you might find it easier to test if you simply inject a Provider<LookupService> instead. Only inject an Injector if you don't know what type you're going to need until runtime--for example, if you implement LookupService.lookup(...) using an Injector by passing the class literal to the injector to get an instance.
In fact, it's hard to say from here, but ILookup seems to act a lot like the Service Locator pattern, which solves the exact type of problem that Guice solves with dependency injection! If that's the case, you might as well rewrite ILookup to use Guice: Just remove calls to LookupService.instance().lookup(Foo.class) and instead create a matching pair of #Inject static Provider<Foo> fooProvider and requestStaticInjection(FooUser.class).
Hope that helps!

DI-Container: Howto pass configuration to objects

Sometimes I have classes which need to get some information for construction. I am not talking about references to other objects (which will be injected) but about (for instance) strings which are holding unique information:
// Scoped as singleton!
class Repository
{
public Repository( InjectedObject injectedObject, string path ) { ... }
}
How do you get this string injected? One possiblity is to write an Init() method and to avoid injection for the string:
class Repository
{
public Repository( InjectedObject injectedObject ) { ... }
public void Init( string path ) { ... }
}
Another possibility is to wrap the information into an object, which can be injected:
class InjectedRepositoryPath
{
public InjectedRepositoryPath( string path ) { ... }
public string Path { get; private set; }
}
class Repository
{
public Repository( InjectedObject injectedObject, InjectedRepositoryPath path ) { ... }
}
This way I'd have to create an instance of InjectedRepositoryPath during the initialisation of my DI-Container and register this instance. But I need such an unique configuration object for every similar class.
Of course I can resolve a RepositryFactory instead of the Repository object, so the factory would ask me for the path:
class RepositoryFactory
{
Repository Create( string path ) { ... }
}
But again, this is one factory just for a singleton object ...
Or, finally, since the path will be extracted from a configuration file, I could skip passing around the string and read the config in my constructor (which is probably not as optimal, but possible):
class Repository
{
public Repository( InjectedObject injectedObject )
{
// Read the path from app's config
}
}
What's your favorite method? For non-singleton classes you have to use imho the Init() or factory solution, but what about singleton-scoped objects?
If you are using Constructor injection I find adding a parameter which is your configuration object to the constructor is the best way. By using an init function you are somewhat sidestepping the point of constructor injection. This makes testing harder, it also makes maintenance and delivery more difficult.
Discovery becomes a problem because it is not readily apparent that this class requires a configuration object. By adding it to the constructor anyone using this object knows explicitly that this configuration must be there.
I prefer not having a DI Container dictate my API design. The container should conform to proper design, not the other way around.
Design your classes in a DI-friendly manner, but without making concessions to your DI Container. If you need a connection string, then take a string through the constructor:
public class Repository : IRepository
{
public Repository(string path) { //... }
}
Many DI Containers can deal with primitive values. As an example, here's one way to do it with Windsor:
container.Register(Component.For<IRepository>()
.ImplementedBy<Repository>()
.DependsOn( new { path = "myPath" } ));
However, if your container of choice can't deal with primitive parameters, you can always decorate Repository with an implementation that knows how to find the string:
public class ConfiguredRepository : IRepository
{
private readonly Repository decoratedRepository;
public ConfiguredRepository()
{
string path = // get the path from config, or whereever appropriate
this.decoratedRepository = new Repository(path);
}
// Implement the rest of IRepository by
// delegating to this.decoratedRepository
}
Now you can simply tell your container to map IRepository to ConfiguredRepository, while still keeping the core Repository implementation clean.

How to bind String to variable in Guice?

I'm new to Guice and here is a naive question. I learned that we could bind String to a particular value through:
bind(String.class)
.annotatedWith(Names.named("JDBC URL"))
.toInstance("jdbc:mysql://localhost/pizza");
But what if I want to bind String to any possible characters?
Or I think it could be described this way:
How can I replace "new SomeClass(String strParameter)" with Guice?
You first need to annotate the constructor for SomeClass:
class SomeClass {
#Inject
SomeClass(#Named("JDBC URL") String jdbcUrl) {
this.jdbcUrl = jdbcUrl;
}
}
I prefer to use custom annotations, like this:
class SomeClass {
#Inject
SomeClass(#JdbcUrl String jdbcUrl) {
this.jdbcUrl = jdbcUrl;
}
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.FIELD, ElementType.PARAMETER})
#BindingAnnotation
public #interface JdbcUrl {}
}
Then you need to provide a binding in your Module:
public class SomeModule extends AbstractModule {
private final String jdbcUrl; // set in constructor
protected void configure() {
bindConstant().annotatedWith(SomeClass.JdbcUrl.class).to(jdbcUrl);
}
}
Then an time Guice creates SomeClass, it will inject the parameter. For instance, if SomeOtherClass depends on SomeClass:
class SomeOtherClass {
#Inject
SomeOtherClass(SomeClass someClass) {
this.someClass = someClass;
}
Often, when you think you want to inject a String, you want to inject an object. For instance, if the String is a URL, I often inject a URI with a binding annotation.
This all assumes there is some constant value you can define at module creation time for the String. If the value isn't available at module creation time, you can use AssistedInject.
This might be off-topic, but Guice makes configuration much easier than writing an explicit binding for every String you need. You can just have a config file for them:
Properties configProps = Properties.load(getClass().getClassLoader().getResourceAsStream("myconfig.properties");
Names.bindProperties(binder(), configProps);
and voilĂ  all your config is ready for injection:
#Provides // use this to have nice creation methods in modules
public Connection getDBConnection(#Named("dbConnection") String connectionStr,
#Named("dbUser") String user,
#Named("dbPw") String pw,) {
return DriverManager.getConnection(connectionStr, user, pw);
}
Now just create your Java properties file myconfig.properties at the root of your classpath with
dbConnection = jdbc:mysql://localhost/test
dbUser = username
dbPw = password
or merge authorization information from some other source into the properties and you're set.
I was able to inject a string through Named annotation.
#Provides
#Named("stage")
String stage() {
return domain;
}
class SomeClass {
#Inject
#Named("stage")
String stageName;
}
I find a solution in the FAQ of Guice:
http://code.google.com/docreader/#p=google-guice&s=google-guice&t=FrequentlyAskedQuestions
In addition to define an annotation and a String attribute in MyModule, I need to write below line to get a instance of SomeClass:
SomeClass instance = Guice.createInjector(new MyModule("any string i like to use")).getInstance(SomeClass.class);
But I remembered that Injector.getInstance() should not be used except for the root object, so is there any better way to do this?

Resources