Configuring and injecting Grails services - grails

Grails 2.4.5 here. I did a grails create-service com.example.service.Simple which created a SimpleService for me, which I then modified to look like so:
class SimlpeService {
SimpleClient simpleClient
Buzz doSomething(String derp) {
// ...
Fizz fizz = simpleClient.doSomething(derp)
// ...
fizz.asBuzz()
}
}
I can now "inject" controllers with SimpleService like so:
class MyController {
SimpleService simpleService
def index() {
// etc...
}
}
But how do I configure/wire SimpleService with the correct SimpleClient instance. Let's pretend SimpleClient is typically built like so:
SimpleClient simpleClient = SimpleClientBuilder
.withURL('http://simpleclientdev.example.com/simple')
.withAuth('luggageCombo', '12345')
.isOptimized(true)
.build()
Depending on what environment I'm in, I may want my SimpleClient instance to connect to simpleclientdev.example.com, simpleclientqa.example.com, or even simpleclient.example.com. Also, I may use different auth credentials, and I might/might not want it to be "optimized", etc. The point is: How do I inject the SimpleService with a SimpleClient instance?

You can use the Java's PostConstruct annotation on one of your method in your service to do the stuff you want. From the docs:
The PostConstruct annotation is used on a method that needs to be
executed after dependency injection is done to perform any
initialization.
SimpleService.groovy
import javax.annotation.PostConstruct
class SimlpeService {
private SimpleClient simpleClient
def grailsApplication
#PostConstruct
void postConstruct() {
def config = grailsApplication.config.client.data
SimpleClient simpleClient = SimpleClientBuilder
.withURL(config.url)
.withAuth('luggageCombo', config.username)
.isOptimized(config.optimized)
.build()
}
Buzz doSomething(String derp) {
// ...
Fizz fizz = simpleClient.doSomething(derp)
// ...
fizz.asBuzz()
}
}
So, Grails or Spring will call this method postConstruct() automatically when all the dependencies (in this case grailsApplication) for this service are resolved and any of the service method is invoked.
This has been taken care that that method must invoke before you access any field member or method of the SimpleService.
Now, everything is already configured like you mentioned that you may need to call different URL with different credential, just you have to define them in your Config.groovy as:
environments {
development {
client {
data {
url = "simpleclientdev.example.com"
username = "test"
optimized = false
}
}
}
production {
client {
data {
url = "simpleclient.example.com"
username = "johndoe"
optimized = true
}
}
}
}
Now when you do run-app with development mode and calling simpleService.doSomething() in your example controller will automatically hit the simpleclientdev.example.com URL with test credential and when you deploy it using production environment, the same simpleService.doSomething() method will hit simpleclient.example.com with optimized set to true.
Update
The key point here based on your question is that we will not be injecting different instances of SimpleService since services are singleton by default. Instead we are changing the value's associated with the service based on the environment.

Sounds like you need to understand a bit more about how to leverage Spring maybe?
Grails Spring Docs
You can also do things like so in your Service class
#PostConstruct
void init() {
//configure variables, etc here ...
log.debug("Initialised some Service...")
}

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.

Accessing Grails services from src/groovy

Grails services are abstractions used for implementing business logic (as well as connecting to backing services/DBs, etc.) outside of a controller. So in a typical controller you might have:
class DashboardController {
StatisticsService statsService
def index() {
// Fetches all the stats that need to be displayed to the
// admin on the dashboard.
AdminDashboardMetrics adm = statsService.getAdminStats()
render(view: "/dashboard", model: [ adm: adm ])
}
}
Here, Grails automatically injects the DashboardController with a bean instance of StatisticsService (provided of course that the service was properly created with grails create-service ...).
But what happens when I need to access StatisticsService outside of a controller, and particularly, under src/groovy?
// src/groovy/com/example/me/myapp/FizzBuzzer.groovy
class FizzBuzzer {
StatisticsService statsService
FizzBuzzer(StatisticsService statsService) {
super()
this.statsService = statsService
}
def doSomething(MyData input) {
MoreData result = statsService.calculate(input)
// use 'result' somehow, etc....
}
}
How do I properly inject FizzBuzzer with the same StatisticsService instance ad what is passed into DashboardController?
You can inject grails service in spring bean by defining injection login in resources.groovy under conf > spring
As I made an ExampleService and Example class in src/groovy
ExampleService
class ExampleService {
def serviceMethod() {
println "do something"
}
}
Example Class under src/groovy
class Example {
ExampleService exampleService
def doSomething() {
def result = exampleService.serviceMethod()
}
}
resources.groovy under conf>spring
beans = {
ex(Example){ bean ->
exampleService = ref('exampleService')
}
}
so I can define Example ex as spring bean in grails-app and it will have ExampleService injected by itself.
Hope this helps. Thanks
You can also get a service using grails.util.Holders
Example:
For injecting MyService service class, use Holders.applicationContext.getBean("myService")
Where "myService" is the name of your service class in lower camel case.
One way of achieving this is by using ServletContext-
ApplicationContext ctx = (ApplicationContext)ServletContextHolder.
getServletContext().getAttribute(GrailsApplicationAttributes.APPLICATION_CONTEXT)
statisticsService = (StatisticsService ) ctx.getBean("statisticsService ")
see this blog - http://www.grailsbrains.com/availing-grails-goodies-in-srcjava-or-srcgroovy/

Best way for a Grails Service class to detect application shutdown?

I have a Grails service class that needs to do some cleanup when my Tomcat application server is shut down.
I don't see anything in the Grails docs about a service.stop() or destroy() method, or a way to implement any sort of application lifecycle listener.
What's the best way to do this?
Thanks!
You have a couple of options
Make your service implement org.springframework.beans.factory.DisposableBean
class MyService implements org.springframework.beans.factory.DisposableBean {
void destroy() throws Exception {
}
}
Or use an annotation
class MyService {
#PreDestroy
private void cleanUp() throws Exception {
}
}
IMO, the annotation option is preferable, because you can give your destructor method a more meaningful name than destroy and your classes public API doesn't expose the Spring dependency
The grails-app/conf/BootStrap.groovy can be used when the app starts and stops.
def init = {
println 'Hello World!'
}
def destroy = {
println 'Goodnight World!'
}
Note: When using development mode grails run-app on some OS's CTL+C will kill the JVM without the chance for a clean shutdown and the destroy closure may not get called. Also, if your JVM gets the kill -9 the closure wont run either.
I would try injecting the service into the Bootstrap and then calling the method from the destroy block, since the destroy block is executed when the application is terminated, something like this:
class BootStrap {
def myService
def init = {servletContext ->
}
def destroy = {
myService.cleanUp()
}
}
It's not quite the same as a service disposal method, but what I ended up doing is registering a Spring Bean with a shutdown method that gets called when the app is stopped.
First, create a bean class, like grails-app/utils/MyShutdownBean.groovy that looks like the following (there's nothing sacred about the class name or the method name, use whatever you want):
class MyShutdownBean {
public void cleanup() {
// Do cleanup stuff
}
}
Then register the bean in grails-app/conf/spring/resources.groovy like this:
beans = {
myShutdownHook(MyShutdownBean) { bean ->
bean.destroyMethod='cleanup'
}
}
If you want to only do the cleanup in production, you can register it like this instead:
beans = {
if (!grails.util.GrailsUtil.isDevelopmentEnv()) {
myShutdownHook(MyShutdownBean) { bean ->
bean.destroyMethod='cleanup'
}
}
}

Can I inject a service into a filter in Grails?

I have a service to get and set the user in the session. I would like to pass in some user information to each view if there is a logged in user and thought a filter would be the best way so I don't have to duplicate that in every controller/action. When I run the app, it get this error:
Error creating bean with name 'userService': Scope 'session' is not active for the current thread
My filter looks like this:
class SecurityFilters {
def userService
def filters = {
setUser(controller:'*', action:'*') {
before = {
if (userService.isLoggedIn()) {
request.user = userService.getUser()
} else {
request.user = null
}
}
}
}
}
I know I can just ultimately access the user through session.user, but I want to be able to call userService.isLoggedIn() which I can't easily through the view. So is there a way to inject the service into the filter, or should I just create a taglib to wrap userService.isLoggedIn()?
It looks like the problem is your userService is scoped to the session, and there is not necessarily a session at the time the attempt to inject the service into the filter happens.
If your userService is necessarily session-scoped then you need to use a scoped proxy in your spring config. E.g. in grails-app/conf/spring/resources.groovy:
import com.your.service.UserService
...
userServiceSession(UserService)
{ bean ->
bean.scope = 'session'
}
userServiceSessionProxy(org.springframework.aop.scope.ScopedProxyFactoryBean)
{
targetBeanName = 'userServiceSession'
proxyTargetClass = true
}
Then rename your injected variable in your SecurityFilter:
def userServiceSessionProxy
(and obviously rename where you use it elsewhere in the class).
What this should do is inject the proxy at injection time, but only go to the actual service at the time the filter is executed (when there is a session).
Note: not sure whether doing this still lets other places where there will be a session (e.g. controllers) still reference the service as 'userService', if not you might be able to rename userServiceSession to userService in resources.groovy (and update targetBeanName accordingly).
It looks like there are some problems with services in filters. This bug might point you in the right direction: https://jira.grails.org/browse/GRAILS-5982
You can also try to get a reference to your service through the ApplicationContext. Here's an example: How do I get an instance of a Grails service programmatically?
import org.codehaus.groovy.grails.web.context.ServletContextHolder
import org.springframework.context.ApplicationContext
import org.springframework.web.context.support.WebApplicationContextUtils
class SecurityFilters {
def userService
def filters = {
setUser(controller:'*', action:'*') {
before = {
def servletCtx = ServletContextHolder.getServletContext()
ApplicationContext applicationContext = WebApplicationContextUtils.
getRequiredWebApplicationContext(servletCtx)
userService =applicationContext.getBean('userService')
if (userService.isLoggedIn()) {
request.user = userService.getUser()
} else {
request.user = null
}
}
}
}
}

How To Call A Taglib As A Function In A Domain Class

I need to call the Static Resources Plugin (http://www.grails.org/Static+Resources+Plugin) from my domain class.
This works perfectly in a controller:
def tstLink = resourceLinkTo(dir:"docs/${identifier}",file:originalFileName)
but in a domain class I get
Exception Message: No signature of method: static org.maflt.ibidem.Item.resourceLinkTo() is applicable for argument types: (java.util.LinkedHashMap) values: [[dir:docs/19e9ea9d-5fae-4a35-80a2-daedfbc7c2c2, file:2009-11-12_1552.png]]
I assume this is a general problem.
So how do you call a taglib as a function in a domain class?
I encountered this problem a while ago for an app I was working on. What I ended up doing was putting a call to the tag in a service method:
class MyService {
def grailsApplication //autowired by spring
def methodThatUsesATag(identifier, originalFileName) {
//This is the default grails tag library
def g = grailsApplication.mainContext.getBean('org.codehaus.groovy.grails.plugins.web.taglib.ApplicationTagLib')
g.resourceLinkTo(dir:"docs/${identifier}",file:originalFileName)
}
}
Then in my domain class, I could get to the service via spring autowiring as well:
class MyDomain {
String originalFileName
def myService //autowired
static transients = ['myService'] //Necessary so that GORM doesn't try to persist the service instance.
//You can create a method at this point that uses your
//service to return what you need from the domain instance.
def myMethod() {
myService.methodThatUsesATag(id, originalFileName)
}
}
Most taglibs rely on data from the controller so it's often not possible to reuse them while others concern view logic so often it's not something you would want to put in a domain class.
That said, I'm sure you have your reasons so maybe the source of the taglib will help:
class ResourceTagLib {
def externalResourceServerService
def resourceLinkTo = { attrs ->
out << externalResourceServerService.uri
out << '/'
if(attrs['dir']) {
out << "${attrs['dir']}/"
}
if(attrs['file']) {
out << "${attrs['file']}"
}
}
}
ie inject the externalResourceServerService into your domain class and the rest should be simple.

Resources