I'm currently using the session() of play framework in my template :
#if(session().get("email")==null){
<li>Login</li>
}else{
<li>Logout</li>
}
This template is used in all of my views. Some of these views are controlled by a Java controller, and some are with a Scala controller.
When I click on links that lead to Java controllers, I have no problems, the links for login and logout are correctly handled.
When I click on links that lead to Scala controllers, I get a [RuntimeException: There is no HTTP Context available from here.]
From what I read in here about scala controllers, I understood that they didn't return the http context when rendering a page, but I really want to be able to use the session in my template.
I thought about using an argument session() in my view, templates and controllers, but I believe that there will be a conflict between the java session (play.mvc.http.session) and the scala session (play.api.mvc.session) when play will compile the html pages.
Am I stuck? Is there a possibility to force scala controllers to give back the http context ?
The root cause maybe the Java controllers and Scala controllers are handled differently.
I have my project in Java first, and then try to add more Scala controllers. I also came across this problem (BTW, I am using Play 2.3.2).
I tried to fix this by setting my own Http.Context in the TheadLocal variable using my own ActionBuilder.
import play.api.mvc._
import scala.concurrent.Future
import play.mvc.Http.Context
import play.core.j.JavaHelpers
object ContextAction extends ActionBuilder[Request] {
def invokeBlock[A](request: Request[A], block: (Request[A]) => Future[Result]) = {
Context.current.set(JavaHelpers.createJavaContext(request))
block(request)
}
}
Then my Scala controller actions simply use this ContextAction instead:
class TestController extends Controller {
def test = ContextAction { implicit request =>
Ok(views.html.index())
}
}
And this way the index template can access all request() / session() / etc.
I may be wrong, but I think that your Scala controllers should look like:
def myaction() = Action { implicit request =>
...
}
instead of:
def myaction() = Action {
...
}
Ie, you have to add the request to the scope of your Action.
And add it also to your view, at the beginning of the file:
#(...)(implicit session:Session)
Okay I found a workaround this problem.
This is not really aesthetic, but it works, and gets rid of the problem entirely.
I created two different main templates : scalamain.scala.html and javamain.scala.html.
The scalamain template is used by all views that are controlled by a Scala controller, and used the usual trick to use the session (implicit arguments, see more here).
The javamain template is used by all view that are controlled by a Java controller. (these view use the session easily).
The two templates are of course, the same once rendered by play.
I end up with some redundancy in my code and it took to separate all the actions so that view are controlled by only one type of controller(scala or java).
I hope this will help others with the same problem. I validate this answer, as it solves the problem, but feel free to answer if you find a more gracious way to solve it.
Related
I have a (web-)application that needs special configurations and/or extensions based on the customer using the application. I call these additions "plugins" and they are auto discovered by classpath scanning when the application starts. For extensions that is incredibly easy. Let's say I want to have a plugin which adds an API that prints "hello world" when the URL /myplugin/greet is called: I just create a #Controller annotated class with the according #RequestMapping, put this in a myplugin.jar, copy that on the classpath and that's it.
Problems come up when I want to change some defaults and especially if I want to do this multiple times. Let's say my core application has a config like this:
#Configuration
public class CoreConfiguration {
#Bean
public Set<String> availableModules() {
return Collections.singleton("core");
}
}
Now I have two plugins that don't know about each other (but they do know the CoreConfig), but they both want to add themselves to the list of available modules. How would I do that? If I only had a single plugin that wants to override the module list I could override the existing bean from CoreConfiguration, but with two plugins that becomes a problem. What I imagine is something like this:
#Configuration
public class FirstPluginConfiguration {
#Bean
public Set<String> availableModules(Set<String> availableModules) {
Set<String> extendedSet = new HashSet<>(availableModules);
extendedSet.add("FirstPlugin");
return extendedSet;
}
}
Of course a SecondPluginConfiguration would look nearly exactly like this, except that the Set is not extended by "FirstPlugin", but by "SecondPlugin". I tested it to check what would happen and spring will just never call the First/SecondPluginConfiguration "availableModules" methods but it does not show an error either.
Now of course in this case this could easily be solved by using a mutable Set in the CoreConfiguration and then autowiring and extending the set in the other configurations, but for example I also want to be able to add method interceptors to some beans. So for example I might have an interface CrashLogger which has a logCrash(Throwable t) method and in CoreConfiguration a ToFileCrashLogger is created that writes stack traces to files as the name suggests. Now a plugin could say that he also wants to get notified about crashes, for example the plugin wants to ADDITIONALLY send the stacktrace to someone by email. For that matter that plugin could wrap the CrashLogger configured by the CoreConfiguration and fire BOTH. A second plugin could wrap the wrapper again and do something totally different with the stacktrace and still call both of the other CrashLoggers.
The later does sound somewhat like AOP and if I'd just let ALL my beans be proxied (I did not test that) I could autowire them into my plugin configurations, cast them to org.springframework.aop.framework.Advised and then add advices that manipulate behaviour. However it does seem like a huge overkill to generate proxies for each and everyone of my beans just so that that plugin can potentially add one or two advices one one or two beans.
I'm new to scala, but have some experience using the play framework in Java. I've added the SecureSocial authentication library, which defines a SecuredACtion, and it seems to be working correctly. However, I'm having trouble understanding the expected content within the custom action in scala code.
Here's my controllers class. Ideally, "index" would simply redirect the authenticated request to "unprotectedIndex" somehow, but that doesn't seem to be possible. So if not, next best thing is simply to serve the file directly from inside of the secured action, but that's also not working.
What is missing from my code?
object Application extends Controller with securesocial.core.SecureSocial {
// this doesn't compile, but it's a long scala exception that I don't know how to fix.
def index = SecuredAction { implicit request =>
Assets.at("/public", "index.html").apply(request)
}
def unprotectedIndex = Assets.at("/public", "index.html")
}
It seems like it's expecting a SimpleResult but getting a Future[SimpleResult] - this feels like it shouldn't be complicated, but what am I missing?
It seems like you are using play framework 2.2. There were some changes and most methods return Future[SimpleResult] instead of just Result or SimpleResult. You can check if you are able to do like this: def index = SecuredAction.async {...} (but I'm almost sure you can't).
You can use this approach to make it work correctly:
import scala.concurrent.Await
import scala.concurrent.duration._
def index = SecuredAction { implicit request =>
Await.result(Assets.at("/public", "index.html").apply(request), 5 seconds) //you can specify you maximum wait time here
}
EDIT
Even one more thing to simplify:
Await.result(unprotectedIndex(request), 5 seconds)
So you can call your unprotectedIndex from your index Action
So, just by looking at syntax highlighting in my IDE I have been able to get something that seems to compile and work but looks deeply wrong to me.
I changed it to this:
def index = SecuredAction { implicit request =>
Assets.at("/public", "index.html").apply(request).value.get.get
}
Is that the correct way to do this? It looks really weird to me, am I just not familiar with the idioms?
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.
To summarize the answer shown here Code assist in (jsp /jstl) view for Spring MVC model objects in Eclipse
is not working for me at all, is there a setting that I need to change ?
I have just downloaded the sample spring-mvc-showcase on github, and it doesn't work out of the box on that project (with either 11.1.3 or EAP 12 version both full enterprise editions), see below (I have no idea where it gets formBean from) :
Here is an example from my own project,the screen shot below (bottom frame) shows my controller adding a string attribute to model and returning correct view name. I would then expect shopString to be offered up as autocomplete option when editing that view, however it is not :
sg is a javascript variable - so great it should be there, but where is "shopString" ?.
Is there a setting I need to change or something else I am missing to get this functionality (using 11.1.3 enterprise edition with all the spring plugins).
It is also failing on spring specific variables :
IS their an open source (one of the spring tutorial projects?) where this definitely works ... or is there a setting I need change in my Intellij install (I have tested with a brand new download of the version 12 EAP) ?
One more screenshot below shows all my spring coifg files set up correctly via autodetection, but the code inspections fails ... this is the spring-mvc-showcase project :
There's a standard way to do this, which is not IntelliJ-specific.
<jsp:useBean id="someModel" scope="request" type="foo.bar.SomeModelClass"/>
The type attribute here does not need to be a concrete class, it can be an interface type as well. Typically you'd put these declarations at the start of your JSP/JSPX files, to provide something like a "declaration of model inputs".
Using JSPs in such a declarative way was recommended in the original book on Spring, in fact (Expert One-on-One J2EE Design and Development.). IntelliJ has been providing full code completion for such pages since at least 7 years.
Note that there are additional relevant convenience features in IntelliJ: if an EL variable reference is marked as undefined, you can press Alt-Enter to select a QuickFix, which will insert a declaration like above. It will even try to figure out the actual type, based on the properties you're accessing.
As I understand Spring, there is no declaration for definitions of variables that you may put into your model. The call model.addAttribute() may add an object to the model, either identified by a parameter or automatically generated by the class name of the object.
So imagine the following case where you have more than one method:
#RequestMapping("foo") public String foo(Model model) {
model.addAttribute("model", new Foo());
return new Random().nextBoolean() ? "page" : "someOtherPage";
}
#RequestMapping("bar") public String bar(Model model) {
model.addAttribute("model", new Bar());
model.addAttribute("model", new Foo());
model.addAttribute("model", new Bar());
return new Random().nextBoolean() ? "page" : "someOtherPage";
}
and the JSP would be something like
<c:out ${model.value} />
Since there is no proper mapping of which controllers may under some circumstances forward to which views, nor what exactly lies within the model, your IDE has no real chance to provide you with proper information.
But to support the IDE in suggesting you some useful information, you can use type hints. Therefore, you have to copy the whole reference of an object, e. g. foo and add a JSP comment like:
<%--#elvariable id="foo" type="com.mycompany.SomeObject"--%>
The warning will vanish and the full IDE support is on your side, allowing you to traverse the fields of foo.
One of the nicest things is that the unused getter warnings will vanish, too. You can directly call the show usages action directly from the JSP or the POJO.
This also works with JSF and particularly within JSF components. Pretty neat feature to have this kind of code completion, showing warnings and errors.
Hope that helps you with your switch to Intellij Idea.
Edit: I also reported this finding to a friend wo wrapped the whole thing into a nice blog entry. Maybe you're interested in reading it: open link
This got fixed in the latest release of intellij 122.694
I faced with similar issue when start writing my own interceptor. Problem was that I start using refference in my view resolver configuration
don't use contruction like this
<bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" ref="prefix"/>
<property name="suffix" ref="suffix"/>
</bean>-
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