Tapestry 5 - decouple page class name from URL - java

Is there any baked-in way, or established Tapestry pattern, to decouple the name of a page Class from the URL which renders it?
My specific problem is that I have a page class in an English codebase but I want the URLs to be in another language.
For example, the Hello.java page should be accessible from www.example.com/hola rather than the standard www.example.com/hello - though it's fine if both of these URLs work.
Ideally I want something like an annotation to configure a different URL name in-place for each individual page class.
Off the top of my head I could solve this myself with a map of URLs to page class names and a custom RequestFilter to do the mapping on each request - but I don't want to reinvent the wheel if there's a baked-in way to do this or a better pattern that anyone can suggest?

Tynamo's tapestry-routing could help you. It depends on how do you want to generate the links to www.example.com/hola and www.example.com/hello
The #At annotation only allows one route per page, but you can contribute all the routes you want via your AppModule, like this:
#Primary
#Contribute(RouteProvider.class)
public static void addRoutes(OrderedConfiguration<Route> configuration, ComponentClassResolver componentClassResolver) {
String pageName = componentClassResolver.resolvePageClassNameToPageName(Home.class.getName());
String canonicalized = componentClassResolver.canonicalizePageName(pageName);
configuration.add("home1", new Route("/home1", canonicalized));
configuration.add("home2", new Route("/home2", canonicalized));
configuration.add("home3", new Route("/home3", canonicalized));
configuration.add("home4", new Route("/home4", canonicalized));
configuration.add("hola", new Route("/hola", canonicalized)); // the last one is going to be use by default to create links to the page
}
The routes are ordered and by default the last one is going to be used to generate the links.
Currently there is no way to avoid using the default route to generate the links.

Tapestry has a LinkTransformer but I've always found the API lacking since you don't have access to the default behaviour. Igor has written a blog post about the LinkTransformer API here
I've always found it necessary to decorate the ComponentEventLinkEncoder so that I can access the default behaviour and tweak it. See ModeComponentEventLinkEncoder.java and AppModule.java for an example which tweaks the default behaviour and does some string manipulation on the URL.
Thiago has created a url rewriter api here but I've never used it myself. I'm pretty sure his solution is based on decorating the ComponentEventLinkEncoder for outbound URLs and a RequestFilter for inbound URLs.

Related

Use placeholders in feature files

I would like to use placeholders in a feature file, like this:
Feature: Talk to two servers
Scenario: Forward data from Server A to Server B
Given MongoDb collection "${db1}/foo" contains the following record:
"""
{"key": "value"}
"""
When I send GET "${server1}/data"
When I forward the respone to PUT "${server2}/data"
Then MongoDB collection "${db2}/bar" MUST contain the following record:
"""
{"key": "value"}
"""
The values of ${server1} etc. would depend on the environment in which the test is to be executed (dev, uat, stage, or prod). Therefore, Scenario Outlines are not applicable in this situation.
Is there any standard way of doing this? Ideally there would be something which maintains a Map<String, String> that can be filled in a #Before or so, and runs automatically between Cucumber and the Step Definition so that inside the step definitions no code is needed.
Given the following step definitions
public class MyStepdefs {
#When("^I send GET "(.*)"$)
public void performGET(final String url) {
// …
}
}
And an appropriate setup, when performGET() is called, the placeholder ${server1} in String uri should already be replaced with a lookup of a value in a Map.
Is there a standard way or feature of Cucumber-Java of doing this? I do not mind if this involves dependency injection. If dependency injection is involved, I would prefer Spring, as Spring is already in use for other reasons in my use case.
The simple answer is that you can't.
The solution to your problem is to remove the incidental details from your scenario all together and access specific server information in the step defintions.
The server and database obviously belong together so lets describe them as a single entity, a service.
The details about the rest calls doesn't really help to convey what you're
actually doing. Features don't describe implementation details, they describe behavior.
Testing if records have been inserted into the database is another bad practice and again doesn't describe behavior. You should be able to replace that by an other API call that fetches the data or some other process that proves the other server has received the information. If there are no such means to extract the data available you should create them. If they can't be created you can wonder if the information even needs to be stored (your service would then appear to have the same properties as a black hole :) ).
I would resolve this all by rewriting the story such that:
Feature: Talk to two services
Scenario: Forward foobar data from Service A to Service B
Given "Service A" has key-value information
When I forward the foobar data from "Service A" to "Service B"
Then "Service B" has received the key-value information
Now that we have two entities Service A and Service B you can create a ServiceInformationService to look up information about Service A and B. You can inject this ServiceInformationService into your step definitions.
So when ever you need some information about Service A, you do
Service a = serviceInformationService.lookup("A");
String apiHost = a.getApiHost():
String dbHost = a.getDatabaseHOst():
In the implementation of the Service you look up the property for that service System.getProperty(serviceName + "_" + apiHostKey) and you make sure that your CI sets A_APIHOST and A_DBHOST, B_APIHOST, B_DBHOST, ect.
You can put the name of the collections in a property file that you look up in a similar way as you'd look up the system properties. Though I would avoid direct interaction with the DB if possible.
The feature you are looking for is supported in gherkin with qaf. It supports to use properties defined in properties file using ${prop.key}. In addition it offers strong resource configuration features to work with different environments. It also supports web-services

how to publish multiple rest endpoints with same base address?

first our scenario:
we have an OSGI environment, where several bundles publish their own rest endpoint, e.g.:
http://localhost:8080/api/cars
http://localhost:8080/api/food
http://localhost:8080/api/toys
This was done using JAXRSServerFactoryBean.create() method, with address being the ones listed above.
Now we need to add a tenant id to the users requests (not user auth, which is different, as users may be part of several tenants). URLs should look like this:
http://localhost:8080/api/tenant/{tenantid}/cars
http://localhost:8080/api/tenant/{tenantid}/food
http://localhost:8080/api/tenant/{tenantid}/toys
I tried two approaches to achieve this now:
Add tenant-id to address of service (http://localhost:8080/api/tenant/{tenantid}) - Result: I could access my service under the given URL, but I couldn't fill any data for tenantid but had to type {tenantid} in the URL, which is not how I need to use it.
Publish all three services under the same URL (http://localhost:8080/api) moving the tenant-part to the #Path annotation of each api class - Result: Exception, that address was already taken by other endpoint
Does anyone have an idea, how this can be done properly? I know that the ServiceBean can take an array of implementors as an argument instead of a single class, but this is not an option, as the bundles load separately and I had some dependency issues, when I tried make this "all in one".
As a sidenote: I know, we could put tenant id in a header, but typically tenant info is somewhere in a URL (host or path) and we wanna go with this "common" style instead of adding a custom header, though implementation of header style would be much easier (already got it to work).
Any ideas would help.
Thanks,
Kay
Try something like:
#Path("/tenants")
public class TenantResource{
#Path("/{tenantId}/cars")
#Get
public List<Car> getTenantCars(#PathParam("tenantId") long tenantId){...}
#Path("/{tenantId}/food")
#Get
public Food getTenantFood(#PathParam("tenantId") long tenantId){...}
#Path("/{tenantId}/toys")
#Get
public List<Toy> getTenantToys(#PathParam("tenantId") long tenantId){...}
}
If you have URLs such as tenants/{tenantid}/cars then this usually means "the cars of the tenant with id = tenantid".
"cars" is a property of the "tenant" resource and thus should be in the same resource.
I think it might be hard to modularize properties of a resource/ object.
But you could consider a "car" resource and query the resource like: /cars?tenantid={tenantid}
#Path("/cars")
public class CarResource{
#Get
public List<Car> getCarsByTenantId(#QueryParam("tenantId") long tenantId){...}
}
or similar.

Spring MVC : Generating links which match the controller url pattern

I am writing a web app with Spring 4.0.
I have written my controllers in what I believe to be the normal way using the #RequestMapping annotation to define the url pattern which the controller handles.
The snippet below illustrates this for a controller which displays a testimonial ...
#Controller
#RequestMapping("/testimonialView")
public class TestimonialRequestController {
#RequestMapping(value="/{testimonialName}", method=RequestMethod.GET)
public ModelAndView testimonialRequest(#PathVariable String testimonialName, ModelAndView modelAndView) throws FileNotFoundException {
Testimonial testimonial;
. . .
}
}
Elsewhere in my application I want to generate a link bar which includes all the testimonials to include in my left hand nav.
At the moment, when I construct the href for the anchor element to go into the link bar, I am hardcoding the url, like this:
String href="/testimonialView/" + testimonialName;
This does not seem right. If later on I want to change the url structure I have to change it in at least two places - possibly more. Once where the incoming URL is matched to the controller, and once to construct the anchor element which a user will click to invoke that URL.
Is there a best practice way of dealing with this problem? It must be a common one. Is it as simple as using Constants to represent the URLs and accessing these from different places? I know my example is simple but I am assuming the problem must exist for much larger web apps with complex URL structure so I want to understand what best practice is.
I hope this isn't a stupid question. I am keen to ensure that I implement best practice right from the beginning. I have looked through Stackoverflow and Google but nothing quite answers this specific question.
Any help gratefully received.
The short answer is that you can't do this dynamically because #RequestMapping puts data into the code at compile time.
However, there are a couple of options that work.
You can define the string constants in a separate class - this will make it easier for you to change the names of URLs
You can explore the request mappings at runtime within Spring, so could have some code that found URLs you'd coded elsewhere - I've done this for identifying when a URL is dynamic content, vs coded content.
My recommendation is
public class URLs {
public static final String TESTIMONIAL_VIEW = "/testimonialView";
}
with
#RequestMapping(URLs.TESTIMONIAL_VIEW)
and
String href= URLs.TESTIMONIAL_VIEW + "/" + testimonialName;
There isn't any better practice for this afaik. Most you can do is, as Ashley said, is to use constants. But as with any other client-server situation such as the Web, if you change the contract (the url in this case) you'll have to do so for both the client (i.e. the links) and the server (the controller mappings).
I would also mention that your controller can be more general, for example have a "Testimonials" controller and "view/{name}" as an action within that controller.
Hope this helps

How to make server-side domain models available to client-side web browser?

In my web application utilizing Spring MVC, I have a rich domain model.
I would like to make this domain model available to a client web browser. For example, as my domain model includes a class Person with methods Set<Person> getFriends() and DateTime getBirthday(), I would like to use these methods on the client side. Usage scenarios include
dynamically updating the visiting browser's HTML to list all friends when requested so by the user, or
sort persons in the HTML by their birthday.
Please notice I'm not looking here for accessing my domain model in the "view rendering stage" (e.g. JSP). I am looking here for accessing my domain model on my web application's users' browsers. So for example I don't want to sort Person instances during the "view rendering stage". I want this sorting to happen later, on my user's browser.
What are solutions to my challenge?
Javascript - there are frameworks that could help ease the burden. The scenario you have described is an Ajax call to some service. You could represent the data as json which would be lightweight and easy enough to add to the page using javascript.
Ember.js (specifically its Models) and Grails do exactly what you want when used together. I'm sure that you can use any Java framework to do this, but Grails makes it stupidly easy. Below are a few patterns to get you started, but here's a complete example app!
Domain class:
class Person {
String name
}
Controller:
class PersonsController {
def index() { render (["person": Person.list()] as JSON) }
}
Ember.js App:
App.Store = DS.Store.extend({
revision: 11,
adapter: DS.RESTAdapter.create({
namespace: 'app'
})
)};
App.Person = DS.Model.extend({
name: DS.attr('string')
)};
In your browser, this single command will populate the in-browser data store by fetching /app/persons from the backend. Modifying the in-browser instances will automatically HTTP POST the updated instance to your Controller.
App.Person.list()
You'll want to check out my answer on getting the two to play together in perfect harmony for more complex applications.
Abdull have you looked at GWT(Google Web ToolKit) http://bit.ly/YYz2Yx?
Here is some sample code that illustrates creation of client side components.
e.g. loading contacts
VerticalPanel contactsPanel = new VerticalPanel();
contactsPanel.setSpacing(4);
String[] contactNames = constants.cwStackPanelContacts();
String[] contactEmails = constants.cwStackPanelContactsEmails();
for (int i = 0; i < contactNames.length; i++) {
final String contactName = contactNames[i];
final String contactEmail = contactEmails[i];
final Anchor contactLink = new Anchor(contactName);
contactsPanel.add(contactLink)
http://bit.ly/12MOhZQ (for actual code sample)
If you were not limited to the browser - and thus javascript, I'd scream RMI about now. Luckily, there seems to be a solution to make it work. I have not tried it, but it might be worthwhile:
jabsorb is a simple, lightweight JSON-RPC library for communicating
between Javascript running in the browser and Java running on the
server. It allows you to call Java methods on the server from
Javascript as if they were local, complete with automatic parameter
and result serialization.
https://code.google.com/p/jabsorb/
Two populer javascript MVC frameworks:
http://backbonejs.org/
http://knockoutjs.com/
You can try them personally. suggestion are always subjective, your choice are always subjective as well. so just feel it yourself.

How do I make session/cache variables available to my main.html template?

I'm using Play Framework and setting a cache value as such:
String sessionId = Scope.Session.current().getId();
Cache.set(sessionId + "_user", "Doser");
and I want to ouput the value in my main.html without adding the value to every single controller in my application.
How do I achieve this in Play?
The other option you have for this, is to create an action in your controller that uses the #Before annotation, and then add the value using renderArgs().
I answered a previous question which I think is very similar to your requirements.
Does Play Framework support "snippets"?
You should also be aware that all session variables are available within your template, by default. You can see all the implicit objects that are available in the template in the Template Documentation here -- http://www.playframework.org/documentation/1.2.2/templates#implicits.
I need to stop answering my own questions.
I've created a tag as described in the link below, and it works perfectly:
http://www.playframework.org/documentation/1.2.2/templates#tags

Categories

Resources