How can a custom filter be added to the HttpPipeline using afBedSheet? - fantom

The afBedSheet documentation says that "Filters can be used to address cross cutting concerns such as authorisation." and shows this code snippet:
#Contribute { serviceType=HttpPipeline# }
static Void contributeHttpPipeline(OrderedConfig conf, AuthFilter myAuthFilter) {
conf.addOrdered("AuthFilter", myAuthFilter, ["after: BedSheetFilters"])
}
I'm trying to implement an AuthFilter (a dummy now but to be evolved into a real AuthFilter) but I can't get it to work:
using afBedSheet
const mixin AuthFilter : HttpPipelineFilter { }
internal const class AuthFilterImpl : AuthFilter {
internal new make(|This|in) { in(this) }
override Bool service(HttpPipeline handler) {
echo("Dummy AuthFilter invoked!")
return handler.service
}
}
I can successfully build the project, but when I run it and invoke any HTTP service I get the error below. I believe I have to declare or annotate my AuthFilter as an afIoc service, but don't know where or how. Can anyone show me how, please?
Thread Locals:
ThreadStash.01.BedSheetModule.0017.perThreadState: fan.afIoc.ModuleServices#1ab5e0b
ThreadStash.01.HttpRequest.0001.headers: fan.afBedSheet.HttpRequestHeaders#1ab6087
afIoc::ThreadStash.counter: 13
web.req: fan.wisp.WispReq#4e62cc
web.res: fan.wisp.WispRes#387bc2
web.session: 060f9951-41f1-e500-0fc2-0019b971d079
[05:27:18 05-Nov-13] [err] [web] Internal error processing: /info
afIoc::IocErr: No dependency matches type mt::AuthFilter.
Ioc Operation Trace:
[ 1] Locating dependency by type 'afBedSheet::HttpPipeline'
[ 2] Creating REAL Service 'HttpPipeline'
[ 3] Creating Service 'HttpPipeline' via a builder method 'afBedSheet::BedSheetModule.buildHttpPipeline'
[ 4] Determining injection parameters for afBedSheet::HttpPipeline buildHttpPipeline(afBedSheet::HttpPipelineFilter[] filters, afIoc::PipelineBuilder bob, afIoc::Registry reg)
[ 5] Looking for dependency of type afBedSheet::HttpPipelineFilter[]
[ 6] Gathering ORDERED configuration of type afBedSheet::HttpPipelineFilter[]
[ 7] Determining injection parameters for sys::Void contributeHttpPipeline(afIoc::OrderedConfig conf, mt::AuthFilter
myAuthFilter)
[ 8] Looking for dependency of type mt::AuthFilter
Stack Trace:
afIoc::Utils.stackTraceFilter (Utils.fan:63)
afIoc::RegistryImpl.dependencyByType (RegistryImpl.fan:243)
afBedSheet::BedSheetWebMod.onService (BedSheetWebMod.fan:34)
wisp::WispActor.doService (WispActor.fan:197)
wisp::WispActor.process (WispActor.fan:78)
wisp::WispActor.receive (WispActor.fan:48)
concurrent::Actor._dispatch (Actor.java:228)
concurrent::Actor._work (Actor.java:199)
concurrent::ThreadPool$Worker.run (ThreadPool.java:255)

AuthFilter does not need to be declared as a service - you could new up an instance in the contribute() method:
#Contribute { serviceType=HttpPipeline# }
static Void contributeHttpPipeline(OrderedConfig conf) {
authFilter := AuthFilterImpl()
conf.addOrdered("AuthFilter", authFilter, ["after: BedSheetFilters"])
}
If AuthFilter has dependencies on other services (for example, it has fields annotated with #Inject) then afIoc should build the instance for you. The OrderedConfig and MappedConfig objects have a handy autobuild() method just for this:
authFilter := conf.autobuild(AuthFilterImpl#)
If you want AuthFilter to be injected into other services, or you want to contribute to it, then it needs to be defined as an afIoc service. Use the bind() method in your AppModule to do this:
static Void bind(ServiceBinder binder) {
binder.bind(AuthFilter#, AuthFilterImpl#)
}
It may then be parameter injected into the contribute method, just as you had in your example:
#Contribute { serviceType=HttpPipeline# }
static Void contributeHttpPipeline(OrderedConfig conf, AuthFilter authFilter) {
conf.addOrdered("AuthFilter", authFilter, ["after: BedSheetFilters"])
}
See afIoc - definingServices for more details. The BedSheetModule may also be a good reference for afIoc examples.

Related

Exposing link on collection entity using spring hateoas

I have pretty the same question as it has been asked here (Exposing link on collection entity in spring data REST). But nothing from that topic helps me to add custom link to collection call.
#Component
public class EventListResourceProcessor implements ResourceProcessor<Resources<Event>> {
#Autowired
private RepositoryEntityLinks entityLinks;
#Override
public Resources<Event> process(Resources<Event> events) {
events.add(entityLinks.linkToCollectionResource(Event.class).withRel("events"));
return events;
}
}
process method is never called in this case.
I need to call http://localhost:8080/event and get the following JSON with my_custom_link under _links section:
{
"_embedded": {
"event": [
{
"id": "1",
"name": "Updated event"
}]
},
"_links": {
"self": {
"href": "http://localhost:8080/event"
},
"profile": {
"href": "http://localhost:8080/profile/event"
},
"my_custom_link": {
"href": "http://localhost:8080/custom/"
}
},
"page": {
"size": 20,
"totalElements": 4,
"totalPages": 1,
"number": 0
}
}
}
Could you please advise me?
Thanks in advance!
I was in a similar situation to your question: I had read through the question/answers you linked and found none of them could solve the problem. Here was the eventual answer to my problem:
#Component
public class MyResourceProcessor implements ResourceProcessor<Resource<MyInterface>> {
#Autowired
private EntityLinks entityLinks;
#Override
public Resource<MyInterface> process(Resource<MyInterface> event) {
event.add(entityLinks.linkForSingleResource(MyClass.class, event.getContent().getId()).slash("custom").withRel("custom"));
return event;
}
}
This process method was called for each Resource in the relevant collection of Resources being returned. The MyInterface interface and MyClass class should be replaced with whatever you end up needing, but it was required to be written that way to get this to work. Here are the steps I used to get the process method called correctly and to determine the MyInterface type.
I created a process method that simply took ResourceSupport as the parameter. I created a breakpoint in the code and inspected what underlying class was extending ResourceSupport. In my case it was PersistentEntityResource. This explained why using Resource<MyClass> or Resources<MyClass> were never causing the process method to be called: PersistentEntityResource extends Resource<Object>.
I updated the process method take PersistentEntityResource as the parameter. This caused the process method to be called for more than my intended changes. I once again used the breakpoint to inspect the PersistentEntityResource object with the intent of discovering what class could be found in the Resource<Object> that it extended. I discovered it was a Proxy class, and could not be cast to MyClass as I had desired.
I followed the answer found here to find out more information about the Proxy class: https://stackoverflow.com/a/3344386/1417690. While debugging, I discovered the list of interfaces that helped define this class. One of them was of type MyProjectionInterface. I now knew that the reason I couldn't use Resource<Portal> was because it was actually a Resource<MyProjectionInterface>.
I had three different Projections that I needed to handle. Instead of creating three separate ResourcePorcoessors I created the MyInterface and had all three of my projection interfaces extend it. The MyInterface only contained a Long getId() method which all of the projections already supported anyway.
I updated my code to use Resource<MyInterface> and added the linkForSingleResource using MyClass (that all of the projections relate to) and the getId() method I had defined in MyInterface. This successfully added the desired link to each of the resources being returned.
Hopefully these steps help others discover how to determine what type to use as the parameter for the process method.
Because I faced the same issue and I hoped an answer here. The solution is to declare a bean ResourceProcessor implementing the right generic type which extends ResourceSupport.
In our case for a paginable resource, the right ResourceSupport is PagedResources<Resource<MyInterface>> as the following sample :
#Bean
public ResourceProcessor<PagedResources<Resource<Supplier>>> pageableSupplierProcessor() {
return new ResourceProcessor<PagedResources<Resource<Supplier>>>() {
#Override
public PagedResources<Resource<Supplier>> process(PagedResources<Resource<Supplier>> resources) {
Method registerMethod;
try {
registerMethod = SupplierRegistrationController.class.getMethod("register",
RegistrationRequest.class);
Link registerLink = linkTo(registerMethod, new RegistrationRequest()).withRel("register");
resources.add(registerLink);
return resources;
} catch (NoSuchMethodException | SecurityException e) {
throw new IllegalStateException(e);
}
}
};
}

Why afBedSheet application throws 'Service does not exist for Type afBedSheet::Routes'

Using the example from afBedSheet's documentation:
using afIoc
using afBedSheet
class HelloPage {
Text hello(Str name, Int iq := 666) {
return Text.fromPlain("Hello! I'm $name and I have an IQ of $iq!")
}
}
class AppModule {
#Contribute { serviceType=Routes# }
static Void contributeRoutes(Configuration conf) {
conf.add(Route(`/index`, Text.fromPlain("Welcome to BedSheet!")))
conf.add(Route(`/hello/**`, HelloPage#hello))
}
}
class Example {
Int main() {
return afBedSheet::Main().main([AppModule#.qname, "8080"])
}
}
If I add this line as the first line of the main() method in the Example class
registry := IocService([AppModule#]).start.registry
The afBedSheet application won't start and throws:
[21:40:10 11-Aug-14] [info] [afIoc] Adding module definition for Example_0::AppModule
[21:40:10 11-Aug-14] [info] [afIoc] Starting IOC...
[21:40:10 11-Aug-14] [err] [afIoc] Err starting IOC
afIoc::IocErr: Service does not exist for Type 'afBedSheet::Routes' defined in contribution method Example_0::AppModule.contributeRoutes.
Ioc Operation Trace:
[ 1] Building IoC Registry
[ 2] Validating contribution definitions
Stack Trace:
afIoc::Utils.stackTraceFilter (Utils.fan:45)
afIoc::RegistryBuilder.build (RegistryBuilder.fan:108)
afIoc::IocService.onStart (IocService.fan:76)
fan.sys.Service$.start (Service$.java:206)
afIoc::IocService.start (IocService.fan:10)
Example_0::Example.main (/Users/coder/Documents/temp/testBedSheet/Example.fan:20)
java.lang.reflect.Method.invoke (Method.java:483)
fan.sys.Method.invoke (Method.java:559)
fan.sys.Method$MethodFunc.callOn (Method.java:230)
fan.sys.Method.callOn (Method.java:139)
fanx.tools.Fan.callMain (Fan.java:175)
fanx.tools.Fan.executeFile (Fan.java:98)
fanx.tools.Fan.execute (Fan.java:37)
fanx.tools.Fan.run (Fan.java:298)
fanx.tools.Fan.main (Fan.java:336)
ERROR: fan.afIoc.IocService.onStart
How can I access the IocService of the afBedSheet app to get bound services?
There is no need to create your own IoC Registry as BedSheet creates one for you.
Also, there should not be any need to access the registry outside of the web application.
If you need something to happen when the web app starts, contribute a func / closure to RegistryStartup:
class AppModule {
#Contribute { serviceType=RegistryStartup# }
static Void contributeRegistryStartup(Configuration conf, MyService myService) {
conf.add |->| {
myService.soSomething()
}
}
}
To access during a web request, #Inject services into your handlers:
class HelloPage {
#Inject MyService? myService
Text hello(Str name, Int iq := 666) {
myService.doSomething()
return Text.fromPlain("Hello! I'm $name and I have an IQ of $iq!")
}
}

Grails RESTful API Plugin - wrong service siginiture

I am trying to use Restful API plugin (Restful API plugin). (using Grails 2.3.8, Groovy 2.1)
As stated in documentation I created a Grails service that implements RestfulServiceAdapter.
import net.hedtech.restfulapi.RestfulServiceAdapter
import com.game.trivia.Question
#Transactional
class QuestionService implements RestfulServiceAdapter {
#Override
public Object list(def service, Map params) throws Throwable{
List Q = Question.list(params)
return Q;
}
.
.
.
When trying to access the service: http://localhost:8080/test_triv/api/questions
I received following exception:
{"errors":[{"type":"general",
"errorMessage":"No signature of method: test_triv.QuestionService.list() is applicable for argument types:
(org.codehaus.groovy.grails.web.servlet.mvc.GrailsParameterMap) values:
[[pluralizedResourceName:questions, action:[...], ...]]\nPossible solutions: list(java.lang.Object, java.util.Map),
is(java.lang.Object), wait(), find(), wait(long), with(groovy.lang.Closure)"}]}
So I implemented another list method (which is not part of the interface):
public Object list(Map params) throws Throwable {
List Q = Question.list(params)
return Q;
}
Which works ok.
Am I doing something wrong?
Do I implement the correct interface?
Do I have to expose a service for each domain or there is any way to use an existing controller instead of a service?
Creating new service is a big overhead! I already have controllers for all domains.
Just got reply on this issue from Charlie (The pludin developer):
Our documentation should be clearer in this area, so I'll take an action to look at improving it.
You should not implement the RestfulServiceAdapter within a service, but implement and register an adapter that implements this interface if you need to adapt an existing service that does not provide the expected methods.
Since you are writing a new service, you can just expose the required methods (you don't need to implement any interface). Note the contract is essentially the same as the adapter interface, without the 'service' argument that represents the service to which the adapter would delegate.
To avoid needing an adapter, a service should expose these methods:
def list( Map params ) throws Throwable { ... }
def count( Map params ) throws Throwable { ... }
def show( Map params ) throws Throwable { ... }
def create( Map content, Map params ) throws Throwable { ... }
def update( def id, Map content, Map params ) throws Throwable { ... }
void delete( def id, Map content, Map params ) throws Throwable { ... }
The controller is intended to delegate to a service that contains business logic, and it cannot delegate to another controller. Our expectation is the RestfulApiController and other controllers within an application would share services (e.g., a ThingController and the RESTfulApiController could both use the same ThingService so that business logic is not duplicated).

Configure JAX-RS base URI programmatically per deployment

Im trying to use CDI extensions to discover JAX-RS resources at runtime and automatically publish them under different base URIs in a Java SE environment. Applications should not need to extend javax.ws.rs.core.Application themselves if possible.
I have read RestEasy documentation and javadoc but failed to find any obvious way to modify the #ApplicationPath at runtime.
One idea that im exploring is to try generate javax.ws.rs.core.Application and set the #ApplicationPath base URI programmatically, maybe by using an AnnotatedType CDI extension, and publish that as a * org.jboss.resteasy.spi.ResteasyDeployment`.
Are there other/better ways to do this?
EDIT:
Trying CDI extension event ProcessAnnotatedType to change #javax.ws.rs.Path of JAX-RS resources.
<X> void process(#Observes ProcessAnnotatedType<X> pat) {
if (!pat.getAnnotatedType().isAnnotationPresent(javax.ws.rs.Path.class)) {
return;
}
final AnnotatedType<X> org = pat.getAnnotatedType();
AnnotatedType<X> wrapped = new AnnotatedType<X>() {
#Override
public <T extends Annotation> T getAnnotation(final Class<T> annotation) {
if (javax.ws.rs.Path.class.equals(annotation)) {
class PathLiteral extends AnnotationLiteral<javax.ws.rs.Path> implements javax.ws.rs.Path {
#Override
public String value() {
return "change_me/" + (javax.ws.rs.Path) org.getAnnotation(annotation);
}
}
return (T) new PathLiteral();
} else {
return org.getAnnotation(annotation);
}
}
pat.setAnnotatedType(wrapped);
}
... then after bootstrap, constructing the bean using javax.enterprise.inject.spi.BeanManager was expecting the following code to print "change_me/...."
Set<Bean<?>> beans = beanManager.getBeans(jaxrsClass);
for (Bean<?> bean : beans) {
CreationalContext cc = bm.createCreationalContext(bean);
Object jaxrs = bean.create(cc);
Path p = jaxrs.getClass().getAnnotation(Path.class);
System.out.println(p.value());
}
... but this does not work. javax.ws.rs.Path is unchanged for JAX-RS resource 'jaxrsClass'.
What is wrong?
I doubt this can be done in a reliable way. It probably all comes down to which happens first: the CDI bootstrap or JAX-RS, of course in the future or in other application servers it could all be done in parallel.
It's certainly a cool idea though. What have they said on the RestEasy forums?
We are already using such an approach.
We are using the feature to use Subresource locators and take the power of guice.
At the startup we are scanning the classpath for all resources annotated with #Path. After that we are extracting the path and binding the resources with the help of Names/#Named. So the resources can later be injected with the help of the name.
bind(..).annotatedWith(Names.named("path")).to(..)
The next step is that you need a resource with a subresource locator.
#Path("{name}")
public Object find(#PathParam("name") name){
return injector.getInstance(..);
}
You could use this approach to bind them at runtime and also to change the original annotated path.

How can I achieve this in Windsor Castle? (Migrating from StructureMap)

I need to modify an existing web app to use Castle.Windsor as IOC container. It was originally developed with StructureMap.
I am stuck with the following problem.
Lets say I have registered a couple of interfaces and their corresponding implementations:
IFoo -> Foo
IBar -> Bar
Calling container.Resolve<IFoo>() or container.Resolve<IBar>() works just fine. This means that the services are registered correctly.
I have a Web Api class with dependencies on other services, such as IFoo
public class BadRequestErrorHandler : HttpErrorHandler
{
// services
public BadRequestErrorHandler(IFoo foo) {...} // has dependency on IFoo
}
In StructureMap I can call:
var test = ObjectFactory.GetInstance<BadRequestErrorHandler>();
this will resolve the IFoo dependency.
Now this does not work with windsor.
How can this be achieved with windsor?
Thanks!
* EDIT *
I was just able to make it work by explicitely registering the BadRequestErrorHandler.
container.Register(Component.For<BadRequestErrorHandler>());
I am just hoping there is a better way to achieve this, that does not involve registering classes that have dependencies. I have a bunch of them...
* EDIT 2 **
To ease the pain, I added a special method to deal with these concrete types.
public T GetInstanceWithAutoRegister<T>()
{
if (container.Kernel.GetHandler(typeof(T)) == null)
{
container.Register(Component.For<T>());
}
return container.Resolve<T>();
}
public object GetInstanceWithAutoRegister(Type pluginType)
{
if (container.Kernel.GetHandler(pluginType) == null)
{
container.Register(Component.For(pluginType));
}
return container.Resolve(pluginType);
}
not ideal, but at least better than having to explicetly register each type. Hope someone has a better solution
You can achieve what you want by registering an ILazyComponentLoader which is a hook that gets called by Windsor as a "last resort" when a component cannot be resolved.
In your case, the implementation would probably look somewhat like this:
public class JustLetWindsorResolveAllConcreteTypes : ILazyComponentLoader
{
public IRegistration Load(string key, Type service)
{
return Component.For(service);
}
}
-and then it should be registered as such:
container.Register(Component.For<ILazyComponentLoader>()
.ImplementedBy<JustLetWindsorResolveAllConcreteTypes>());
You can read more about it in the docs.