I want to solve the following problem, it's about deleting entities from a database:
The user selects Delete for a certain entity
The is deleted from the database and disappeared from the list
An undo frame appears inside the page (like Twitter Bootstrap alert messages), where the user can choose to undo the deletion.
I don't know how to realize this, because at the moment I solve this that way:
Delete button links to the URL: delete/entity_id
I have written an if-case for this URL in my request handler that deletes the entity
after the deletion is done, I send a response.sendRedirect(/list) so the updated list is shown
This way I cannot send additional data by redirecting it. Normally I would send the extra data by processing them via the template, but with redirect this is not possible.
How is such a sitation solved?
I have few such scenarios in my web application and here is how I solve it
I have a class called message queue which looks like following
public class MessageQueue {
public static Hashtable<String, Object> messages = new Hashtable<String, Object>();
public static void putMessage(String key, Object obj)
{
messages.put(key, obj);
}
public static Object getMessage(String key)
{
if(key == null)
return null;
Object obj = messages.get(key);
if(obj == null)
return null;
messages.remove(key);
return obj;
}
}
Now this class stays in the memory. Before redirects I create some object that is needed after redirect. Create a random Guid as a String and then store this object in messagequeue
I then add this Guid as a parameter of the URL
String justDeletedId = "someId";
String guid = (new Guid()).toString();
MessageQueue.put(guid,justDeletedId);
sendRedirect("\list\?msgid=" + guid);
Now after redirect you can inspect the messageID and remove the object from the messagequeue and do whatever you please
I choose to allow using this object once ... to avoid memory leak
In the current version ... I also have implemented Last Access Eviction policy which uses a quartz job which cleans up this messagequeue periodically
You could use the setAttribute() and getAttribute() methods of HttpSession. After all that's a way how you can pass Java objects over different HTTP requests.
In your case you could create such an Undo object and store it in the session. After the redirect you have described the session object is retrieved and its content is passed to the template.
Related
Problem:
I currently have a grid that displays content of type SomeModel.
When I click an entry of that Grid I would like to navigate to a view that takes an object as its input to display the entries content.
Implementation:
To achive this behaviour I created a DetailLayout like this:
public DetailLayout extends FlexLayout implements HasUrlParameter<SomeModel>{
/* skipped some details */
#Override
public void setParameter(BeforeEvent event, Host parameter) {
/* This is where I expected to be able to handle the object */
}
}
From within the Grid I tried to navigate like this:
addSelectionListener((event) -> {
event.getFirstSelectedItem().ifPresent(somemodel -> {
getUI().ifPresent(ui -> {
ui.navigate(DetailLayout.class, somemodel);
});
});
});
But unfortunately this behaviour is not supported by Vaadin even tho its syntax is perfectly fine.
Question:
Do you know of another way to pass an object while navigation or did I miss a certain part of the official documentation documentation ?
Thank you in advance
Key-Value collection
As discussed in the comments on the other Answer, if you do not wish to expose the ID value as part of the URL, then work behind the scenes by using the key-value collection provided by Vaadin.
Vaadin actually provides key-value collections at three levels of scope:
ContextYour entire web-app at runtime
SessionEach user
UIEach web browser window/tab, as Vaadin supports multi-window web-apps
The app-wide key-value collection is available on the VaadinContext, via getAttribute & setAttribute methods.
VaadinService.getCurrent().getContext().setAttribute( key , value ) ;
The per-user key-value collection is available on the VaadinSession, via getAttribute & setAttribute methods.
VaadinSession.getCurrent().setAttribute( key , value ) ;
➥ The per-browser-window/tab collection (what you want for your needs in this Question) is not quite so readily available. You have to go through an indirect step. On the ComponentUtil class, call setData & getData methods. In addition to passing your key and your value, pass the current UI object.
Component c = UI.getCurrent() ;
String key = "com.example.acmeapp.selectedProductId" ;
Object value = productId ;
ComponentUtil.setData( c , key , value ) ;
Please vote for my ticket # 6287, a feature-request to add setAttribute/getAttribute methods on UI class, to match those of VaadinSession and VaadinContext.
Instead of giving the whole somemodel object as parameter of navigate(), you can pass its id
ui.navigate(DetailLayout.class, somemodel.getId());
And in the DetailLayout.setParameter() you can load the somemodel by its id
#Override
public void setParameter(BeforeEvent beforeEvent, Long someModelId) {
if(someModelId == null){
throw new SomeModelNotFoundException("No SomeModel was provided");
}
SomeModel someModel = someModelService.findById(someModelId);
if(someModel == null){
throw new SomeModelNotFoundException("There is no SomeModel with id "+someModelId);
}
// use someModel here as you wish. probably use it for a binder?
}
If you are using Spring with Vaadin Flow then you could create a #UIScoped bean and add your own fields storing state related to the current browser window/tab. The bean will be available as long as the UI is present.
Hello In my web application I am maintaining list of URL authorized for user in a HashMap and compare the requested URL and revert as per the authorization. This Map has Role as key and URLs as value in form of List. My problem is where I should have this Map?
In Session: It may have hundreds of URLs and that can increase the burden of session.
In Cache at Application loading: The URLs may get modified on the fly and then I need to resync it by starting server again.
In Cache that update periodically: Application level Cache that will update periodically.
I require a well optimized approach that can serve the purpose, help me with the same.
I'm preferring to make it as a singleton Class and Have a thread that updates it periodically .. The thread will maintain the state of the cache .. this thread will be started when you get the fist instance of the cache
public class CacheSingleton {
private static CacheSingleton instance = null;
private HashMap<String,Role> authMap;
protected CacheSingleton() {
// Exists only to defeat instantiation.
// Start the thread to maintain Your map
}
public static CacheSingleton getInstance() {
if(instance == null) {
instance = new CacheSingleton();
}
return instance;
}
// Add your cache logic here
// Like getRole,checkURL() ... etc
}
wherever in your code you can get the cached data
CacheSingleton.getInstance().yourMethod();
I have a page where accounts with alpha permissions may access. The JSP checks the session for a attribute named "AlphaPerm".
But the problem I'm struggling with is if I find a user is messing/abusing the alpha testing permissions, I want to stop him immediately. I can change his permissions in my database right away but that doesn't stop the abuser right away.
A possible solution is checking my database every time my users do something, But I don't want to do that because that would slow the database down.
So how do I kill his session on-the-fly (Creating a admin page is my plan, but how do I get the users session object)? Basically I want to make a admin page so I can BAN a user.
You can keep references to user sessions by implementing an HttpSessionListener. This example shows how to implement a session counter, but you could also keep references to individual sessions by storing them in a context scoped collection. You could then access the sessions from your admin page, inspect their attributes and invalidate some of them. This post may also have useful info.
Edit: Here's a sample implementation (not tested):
public class MySessionListener implements HttpSessionListener {
static public Map<String, HttpSession> getSessionMap(ServletContext appContext) {
Map<String, HttpSession> sessionMap = (Map<String, HttpSession>) appContext.getAttribute("globalSessionMap");
if (sessionMap == null) {
sessionMap = new ConcurrentHashMap<String, HttpSession>();
appContext.setAttribute("globalSessionMap", sessionMap);
}
return sessionMap;
}
#Override
public void sessionCreated(HttpSessionEvent event) {
Map<String, HttpSession> sessionMap = getSessionMap(event.getSession().getServletContext());
sessionMap.put(event.getSession().getId(), event.getSession());
}
#Override
public void sessionDestroyed(HttpSessionEvent event) {
Map<String, HttpSession> sessionMap = getSessionMap(event.getSession().getServletContext());
sessionMap.remove(event.getSession().getId());
}
}
You can then access the session map from any servlet:
Collection<HttpSession> sessions = MySessionListener.getSessionMap(getServletContext()).values();
As far as I understand your question checking against the DB is definitely a bad thing.
Also you must be comparing some values against some other standard values to decide if the user is messing around.
So an alternate to DB checking you can store these values in the user session and check for those values.
Also instead of creating an Admin page (probably a JSP page) I would suggest using a ServletFilter to do this work.
Also One thing I would personally suggest is instead of invalidating the whole session, you should put some restrictions on the users either for some time or till next login (for example restricting the access of some resource).
In Play!, I want to log something after I executed a controller action, using the #Finally annotation. However, I need some data from the database I sent to my view. Is it possible to access this data in the #Finally annotated method?
This is the method in particular:
#Finally
private static void logSomething() {
//System.out.println("User: " + u.first_name);
System.out.println(response);
for (String key : response.headers.keySet()) {
System.out.println(key);
}
}
How can I pass parameters to this? If I put a parameter in the definition, it's always null (cause how should this method even know what data to pass?).. so is it possible?
If you sent it to your view, then it will be available from the renederArgs map.
So, assuming you called your render method in some way like this...
User user = User.findById(someId);
render(user);
Then you should be able to access it in renderArgs as follows
User user = (User)renderArgs.get("user");
In a GWT app I present items that can be edited by users. Loading and saving the items is perfomed by using the GWT request factory. What I now want to achive is if two users concurrently edit an item that the user that saves first wins in the fashion of optimistic concurrency control. Meaning that when the second user saves his changes the request factory backend recognizes that the version or presence of the item stored in the backend has changed since it has been transfered to the client and the request factory/backend then somehow prevents the items from being updated/saved.
I tried to implement this in the service method that is used to save the items but this will not work because request factory hands in the items just retrieved from the backend with applied user's changes meaning the versions of these items are the current versions from the backend and a comparison pointless.
Are there any hooks in the request factory processing I coud leverage to achieve the requested behaviour? Any other ideas? Or do I have to use GWT-RPC instead...
No: http://code.google.com/p/google-web-toolkit/issues/detail?id=6046
Until the proposed API is implemented (EntityLocator, in comment #1, but it's not clear to me how the version info could be reconstructed from its serialized form), you'll have to somehow send the version back to the server.
As I said in the issue, this cannot be done by simply making the version property available in the proxy and setting it; but you could add another property: getting it would always return null (or similar nonexistent value), so that setting it on the client-side to the value of the "true" version property would always produce a change, which guaranties the value will be sent to the server as part of the "property diff"; and on the server-side, you could handle things either in the setter (when RequestFactory applies the "property diff" and calls the setter, if the value is different from the "true" version, then throw an exception) or in the service methods (compare the version sent from the client –which you'd get from a different getter than the one mapped on the client, as that one must always return null– to the "true" version of the object, and raise an error if they don't match).
Something like:
#ProxyFor(MyEntity.class)
interface MyEntityProxy extends EntityProxy {
String getServerVersion();
String getClientVersion();
void setClientVersion(String clientVersion);
…
}
#Entity
class MyEntity {
private String clientVersion;
#Version private String serverVersion;
public String getServerVersion() { return serverVersion; }
public String getClientVersion() { return null; }
public void setClientVersion(String clientVersion) {
this.clientVersion = clientVersion;
}
public void checkVersion() {
if (Objects.equal(serverVersion, clientVersion)) {
throw new OptimisticConcurrencyException();
}
}
}
Note that I haven't tested this, this is pure theory.
We came up with another workaround for optimistic locking in our app. Since the version can't be passed with the proxy itself (as Thomas explained) we are passing it via HTTP GET parameter to the request factory.
On the client:
MyRequestFactory factory = GWT.create( MyRequestFactory.class );
RequestTransport transport = new DefaultRequestTransport() {
#Override
public String getRequestUrl() {
return super.getRequestUrl() + "?version=" + getMyVersion();
}
};
factory.initialize(new SimpleEventBus(), transport);
On the server we create a ServiceLayerDecorator and read version from the RequestFactoryServlet.getThreadLocalRequest():
public static class MyServiceLayerDecorator extends ServiceLayerDecorator {
#Override
public final <T> T loadDomainObject(final Class<T> clazz, final Object domainId) {
HttpServletRequest threadLocalRequest = RequestFactoryServlet.getThreadLocalRequest();
String clientVersion = threadLocalRequest.getParameter("version") );
T domainObject = super.loadDomainObject(clazz, domainId);
String serverVersion = ((HasVersion)domainObject).getVersion();
if ( versionMismatch(serverVersion, clientVersion) )
report("Version error!");
return domainObject;
}
}
The advantage is that loadDomainObject() is called before any changes are applied to the domain object by RF.
In our case we're just tracking one entity so we're using one version but approach can be extended to multiple entities.