I'm working on a simple Java application. Currently, I have two view, one for login in and another once you're logged in.
So my question is, once the user has logged in, should the controller of the login view creates the second view?
The problem is, the first controller needs to know every dependencies of the second view...
Edit : this is not a web application.
If your object needs to instantiate a class, but you don't want it to depend on the details of instantiating the class, inject a factory (as you've suggested).
I like to use interfaces so I can plug in different implementations of my dependencies. Here's an example:
public class RealLoginController implements LoginController {
private LoginViewFactory viewFactory;
public LoginController(LoginViewFactory viewFactory) {
this.viewFactory = viewFactory;
}
public ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse response) {
if (isLoggedIn()) {
return viewFactory.createLoggedInView();
} else {
return viewFactory.createLoggedOutView();
}
}
// ...
}
public class RealLoggedInView implements LoginView {
// Implementation for rendering stuff
}
public class RealLoggedOutView implements LoginView {
// Implementation for rendering stuff
}
public interface LoginViewFactory {
public LoginView createLoggedInView();
public LoginView createLoggedInView();
}
public class RealLoginViewFactory implements LoginViewFactory {
private FooModel fooEngine;
private BarConfig barConfig;
public RealLoginViewFactory(FooModel fooLayer, BarConfig barConfig) {
this.fooEngine = fooEngine;
this.barConfig = barConfig;
}
public LoginView createLoggedInView() {
if (fooEngine.hasBaz()) {
return new RealLoginView(barCongig.getQux());
} else {
return new RealLoginView(barCongig.getQux(),
fooEngine.getBaz());
}
}
public LoginView createLoggedOutView() {
// ...
}
}
public class RealLoginController implements LoginController {
private LoginViewFactory viewFactory;
public LoginController(LoginViewFactory viewFactory) {
this.viewFactory = viewFactory;
}
public ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse response) {
if (isLoggedIn()) {
return viewFactory.createLoggedInView();
} else {
return viewFactory.createLoggedOutView();
}
}
// ...
}
You can then use the controller with any views you like:
public class LoginControllerTest {
public void testSomething() {
// ...
controller = new RealLoginController(fakeViewFactory);
assertHasTag("td#row1", controller.getRenderedStuff());
// ...
}
}
You may be able to avoid the problem (as bpappa suggests) if you don't need complex instantiation logic and your framework knows how to get dependencies for you by name.
I dont think the LoginController should create the SecondView at all. Once the user has logged in the LoginController should fire off an event that login was successful and any Controller that cares about that should take the appropriate action.
If you are using DI you ideally want to inject the View into a Controller.
Not quite sure what you mean with your last statement though so leaving that unanswered.
This is with Spring MVC?
If you're using Spring MVC 2.5 or lower without annotations, then at the end of handleRequest method, a ModelAndView object is returned. This contains the View information (whether it's the view itself as in a Redirect View, or just the name of a view). You could conditionally return a different view based on some logic. For example -
handleRequest(HttpServletRequest request, HttpServletResponse response) {
// get user from session
if (user.isLoggedIn())
return new ModelAndView("loggedInView");
else
return new ModelAndView("notLoggedInView");
}
If this is a new project though, and you have access to Java 1.5, I would instead recommend using the annotations-based Spring MVC, because then you can return whatever the frik you want.
I think a factory might do the trick :
public class LoginViewFactory
{
private depA;
private depB;
public LoginViewFactory(dependencyA, dependencyB)
{
depA = dependencyA;
depB = dependencyB;
}
LoginView create()
{
return new LoginView(depA, depB);
}
}
//and then pass this factory to my controller
new Controller(new LoginViewFactory(new A(), new B());
What do you think?
you should take the same approach as with Spring and have your controller return a view name.
And then have another class handling creating the view based on the view name.
I think your first "view" ( the login page ) can be rather considered a static page, as usually it will be the first accessible page, so probably there's no controller behind it.
But suppose there are some other pages in your web app and say you actually have a controller A ( kind of DispatchController ) that directs the user to the login page.
Once this page is submitted, you'll normally have a different controller ( say LoginController ) that will direct ( if login successful ) the user to your second view page ( logged in page )
So bottom line is : to me it looks like you need two controllers, each with a different responsability.
If your login is a form POST (which it should be) I'd do a redirect to the home page after a successful login.
This is so that if the user hits refresh on the home page after logging in then they don't get that browser warning about resubmitting a POST.
The same thing applies if they click a link from the home page then click the back button.
In Spring you can do that with this syntax.
return new ModelAndView(new RedirectView(homepage, true), model);
The "homepage" parameter would be configured in your spring config and injected as a parameter - it should be a relative URL to the home page on your site.
Because you're redirecting the homepage controller is executed so this deals with your dependencies problem.
Related
I want to allow only a specific user to access their modification page.
For example, I want user 3 to be the only one to able access the url : /user/3/edit
For this, I have put in my SecurityConfiguration.java :
.authorizeRequests()
.antMatchers("/user/{id}/edit").access("#MyClass.checkId(#id)");
MyClass.java is the following:
#Component
public class MyClass{
public boolean checkId(Long id) {
if(id == SecurityUtils.getCurrentUserId()){ //I have this configured and working
return true;
}
return false;
}
}
Yet when go to the following url: user/4/edit logged in as user 3 (these are examples), I cannot seem to enter the checkId method, and nothing happens, and my page loads with everything in it.
Do you have any idea? Is antMatchers.access() the way to go?
Thank you for your time!
You'll need to subclass two classes.
First, set a new method expression handler
<global-method-security>
<expression-handler ref="myMethodSecurityExpressionHandler"/>
</global-method-security>
myMethodSecurityExpressionHandler will be a subclass of DefaultMethodSecurityExpressionHandler which overrides createEvaluationContext(), setting a subclass of MethodSecurityExpressionRoot on the MethodSecurityEvaluationContext.
For example:
#Override
public EvaluationContext createEvaluationContext(Authentication auth, MethodInvocation mi) {
MethodSecurityEvaluationContext ctx = new MethodSecurityEvaluationContext(auth, mi, parameterNameDiscoverer);
MethodSecurityExpressionRoot root = new MyMethodSecurityExpressionRoot(auth);
root.setTrustResolver(trustResolver);
root.setPermissionEvaluator(permissionEvaluator);
root.setRoleHierarchy(roleHierarchy);
ctx.setRootObject(root);
return ctx;
}
There is another solution and it can be accomplish in a very elegant way using Expression-Based Access Control, for this case you can use the #PreAuthorize annotation and inside it validate the principal user id. For example:
#PreAuthorize("#id == principal.userNumber")
#RequestMapping(method = RequestMethod.POST, value = "/user/{id}/edit")
public void userUpdate(Long id){ .. }
Please just make sure that the implementation of the UserDetails interface has the userNumber property.
You can see more information about Expression-Based Access Control
Another approach is to inject the Principal object into the Request handler method like this:
#RequestMapping(method = RequestMethod.POST, value = "/user/{id}/edit")
public void userUpdate(Long id, Principal myPrincipal){
MyUserDetails user = (MyUserDetails) myPrincipal;
if (user.getUserNumber == id) { ... }
....
}
I am currently using Play 2.3 and I have to deal with similar URL mappings:
GET /api/companyId/employeeId/tasks
GET /api/companyId/employeeId/timesheets
etc.
In every GET I need to perform similar logic:
public Promise<Result> getEmployeeTimesheets(Long companyId, Long employeeId) {
return promise(() -> {
if (!companyRepository.one(companyId).isPresent()) {
return notFound("Company doesn't exist");
}
if (!employeeRepository.one(employeeId).isPresent()) {
return notFound("Employee doesn't exist");
}
if (!employeeRepository.employeeWorksForCompany(companyId, employeeId)) {
return forbidden("Employee doesn't work for this company");
}
// some actual logic here
});
}
This code repeats over and over again. So far I used plain old inheritance and moved that repeating code into the parent controller class. It gets the job done, but it certainly isn't perfect solution (because I have to invoke parent method and inspect results manually in every controller action).
Is there some more declarative approach in Play that would automatically handle fragment of URL (/api/companyId/employeeId in our case) and either delegate the execution to an appropriate controller, or return an error response (for example 404 - Not Found).
You said you are calling the method again and again in each controller function instead you can use #With annotation.For ex
create a class CheckUrl.java
public class CheckUrl extends play.mvc.Action.Simple {
public F.Promise<SimpleResult> call(Http.Context ctx) throws Throwable {
String host = request().uri();
if (condition one satisfied) {
return F.Promise.pure(redirect("/someurl"));
}else if (condition two satisfied){
return F.Promise.pure(redirect(controllers.routes.SomeController.index()));
}
}
Place #With(CheckUrl.class) in class to apply to all its function.
#With(CheckUrl.class)
public class MyController extends Controller {
}
and for a particular function
public class MyController extends Controller {
#With(CheckUrl.class)
public static Result index() {
}
}
In the above cases CheckUrl.java is invoked before function in a controller
In my spring application, I have one generic controller class with serve as base class for several other controllers in my application. These derived controllers have this structure:
#Controller
#RequestMapping(value="usuario")
public class UsuarioController extends controller<Usuario> {
public UsuarioController() {
super(Usuario.class);
}
}
I will have one of this for each entity class from my application. In the generic controller, I have two methods for each action (ie.: insert, update, delete, select). this two methods (and the generic controller) have this form:
public class controller<E> {
#Autowired
private service<E> serv;
private final Class<E> entityClass;
#RequestMapping(value="cadastra")
#PreAuthorize("hasPermission(#user, 'cadastra_'+#this.this.name)")
public ModelAndView cadastra() throws InstantiationException, IllegalAccessException {
return new ModelAndView("privado/"+this.entityClass.getName()+"/cadastra", "command", this.entityClass.newInstance());
}
#RequestMapping(value="cadastra", method=RequestMethod.POST)
#ResponseBody
public String cadastra(#ModelAttribute("object") E object, BindingResult result) {
if(serv.cadastra(object))
return "yes";
else
return "not";
}
}
The problem is that when I run the application, and try access the view mapped by this method, I am getting a 404 error.
Anyone can tell what I am doing wrong here?
The problem is that Spring cannot locate the correct JSP to forward the processing. After reviewing your code it seems that you need to change this.entityClass.getName() with this.getName()
I wish to implement dynamically changeable menu (updating whenever annotated method or controller added) for my Spring MVC application.
What i want is to introduce new annotation (#RequestMenuMapping) which will go to #Controller beans and their methods (just like #RequestMapping works).
Heres is what i want, User class, producing menu like
Users
Index | List | Signup | Login
with following code:
#Controller
#RequestMapping("user")
#RequestMenuMapping("Users")
public class User {
#RequestMapping("")
#RequestMenuMapping("Index")
public String index(/* no model here - just show almost static page (yet with JSP checks for authority)*/) {
return "user/index.tile";
}
#RequestMapping("list")
#RequestMenuMapping("List")
public String list(Model model) {
model.addAttribute("userList",/* get userlist from DAO/Service */);
return "user/list.tile";
}
#RequestMapping("signup")
#RequestMenuMapping("Signup")
public String signup(Model model) {
model.addAttribute("user",/* create new UserModel instance to be populated by user via html form */);
return "user/signup.tile";
}
#RequestMapping("login")
#RequestMenuMapping("Login")
public String login(Model model) {
model.addAttribute("userCreds",/* create new UserCreds instance to be populated via html form with login and pssword*/);
return "user/login.tile";
}
}
I think that Spring AOP may help me to pointcut methods with #RequestMenuMapping annotation and via #AfterReturning add something representing web-site menu to model.
But this raises two questions:
How do i get Model instance in #AfterReturning advice method in case it is missing in adviced method (as in .index())?
How do i get all methods (as in java reflection Method) and classes (as in java reflection Class) annotated with #RequestMenuMapping in order to build complete menu index?
I think a better soultion would be a bean post processor to scan all controller classes for the #RequestMenuMapping and a HandlerInterceptor to add the menu items to every model map.
InterceptorDemo:
#Aspect
#Component
public class InterceptorDemo {
#Pointcut("#annotation(org.springframework.web.bind.annotation.RequestMapping)")
public void requestMapping() {
}
#Pointcut("#annotation(you.package.RequestMenuMapping)")
public void requestMenuMapping() {
}
#AfterReturning("requestMapping() && equestMenuMapping()")
public void checkServer(JoinPoint joinPoint,Object returnObj) throws Throwable {
Object[] args = joinPoint.getArgs();
Model m = (Model)args[0];
// use joinPoint get class or methd...
}
}
If you want to intercept Contoller with you own, you can wirte another pointcut and ProceedingJoinPoint object can get what you want.
Q1:
ModelAndView object create at org.springframework.web.servlet.DispatcherServlet.doDispatch()
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// Do we need view name translation?
if (mv != null && !mv.hasView()) {
mv.setViewName(getDefaultViewName(request));
}
So, you can intercept handle method after returing or override the method.
Q2:As far as i know, there are two ways getting annotation methods.
1.Use AOP:
You can declare a pointcut like this:
#Pointcut("#annotation(you.package.RequestMenuMapping)")
public void requestMenuMappingPountcut() {
}
2.Use reflection.
Class clazz = Class.forName(classStr);
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
if (method.isAnnotationPresent(RequestMapping.class)
&& method.isAnnotationPresent(RequestMenuMapping.class)) {
// do something
}
}
I have a create action in a Play! framework controller that should return the HTTP status code Created and redirect the client to the location of the created object.
public class SomeController extends Controller {
public static void create() {
Something something = new Something();
something.save();
response.status = StatusCode.CREATED; // Doesn't work!
show(something.id);
}
public static void show(long id) {
render(Something.findById(id));
}
}
See also method chaining in the Play! framework documentation.
The code above returns the status code 302 Found instead of 201 Created. What can I do to let Play return the correct status (and Location header)?
The reason this is happening, is that once you created your something, you are then telling play to Show your something, through calling the show action.
To achieve this, play is performing a redirect (to maintain its RESTful state), to tell the browser that as a result of calling the create() action, it must now redirect to the show() action.
So, you have a couple of options.
Don't render a response, and let the client side handle where it goes after creating it (not ideal).
Instead of calling show(), simply render it yourself in the create() method...
To use option 2, it may look like the following:
public static void create() {
Something something = new Something();
something.save();
response.status = StatusCode.CREATED;
renderTemplate("Application/show.html", something);
}
Example code for setting the status code in Play framework:
Response.current().status = Http.StatusCode.CREATED;
In the play framework, calling another action performs a redirect except that the action called is not public.
So, here is one of the solutions:
public class SomeController extends Controller {
public static void create() {
Something something = new Something();
something.save();
response.status = StatusCode.CREATED; // Doesn't work!
show(something.id);
}
private static void show(long id) {
render(Something.findById(id));
}
}