Supamiu
Supamiu

Reputation: 8731

Read and write cookies with @Push

In my vaadin application, i need to use @Push, but since i added it, i can't read and write cookies because VaadinService.getSurrentResponse()returns null because of Push. I manager cookies using this class :

import javax.servlet.http.Cookie;

import com.vaadin.server.VaadinResponse;
import com.vaadin.server.VaadinService;

public class CookieManager {
    private VaadinResponse response;

    public CookieManager(VaadinResponse response){
        this.response = response;
    }

    public Cookie getCookieByName(final String name) {
        // Fetch all cookies from the request
        Cookie[] cookies = VaadinService.getCurrentRequest().getCookies();

        // Iterate to find cookie by its name
        for (Cookie cookie : cookies) {
            if (name.equals(cookie.getName())) {
                return cookie;
            }
        }
        return null;
    }

    public Cookie createCookie(final String name, final String value, final int maxAge) {
        // Create a new cookie
        final Cookie cookie = new Cookie(name, value);

        cookie.setMaxAge(maxAge);

        // Set the cookie path.
        cookie.setPath(VaadinService.getCurrentRequest().getContextPath());

        // Save cookie
        addCookie(cookie);          

        return cookie;
    }

    private void addCookie(Cookie cookie){
        response.addCookie(cookie);
    }

    public Cookie updateCookieValue(final String name, final String value) {
        // Create a new cookie
        Cookie cookie = getCookieByName(name);

        cookie.setValue(value);

        // Save cookie
        addCookie(cookie);

        return cookie;
    }

    public void destroyCookieByName(final String name) {
        Cookie cookie = getCookieByName(name);

        if (cookie != null) {
            cookie.setValue(null);
            // By setting the cookie maxAge to 0 it will deleted immediately
            cookie.setMaxAge(0);
            cookie.setPath(VaadinService.getCurrentRequest().getContextPath());
            addCookie(cookie);
        }
    }
}

When i want to create a cookie (like at user's login), i get a nullPointerException because of the VaadinResponse being null.

So i tried to disable Push in constructor and re-enable it at the end of addCookie()method, but it disabled push for all of my application, even if i re-enable it just after the addCookiemethod.

I saw a ticket on vaadin's trac (http://dev.vaadin.com/ticket/11808) saying that will not be fixed, and someone suggested to create a regular AJAX query from server to create cookie, but i really don't know how to do.

How can i manage my cookies? i need to create AND get cookies, so javascript can't help me there, because i can't get javascript's return in vaadin, so i can't get a cookie.

Upvotes: 2

Views: 2008

Answers (3)

radekpakula
radekpakula

Reputation: 109

Here is my solution how to store cookie when @Push is using. First we create container to storage all instance of client UI. ( This container itself has a great potential)

public class UISession {

private List<WebAppUI> uis = new ArrayList<WebAppUI>();

public void addUI(WebAppUI webAppUI) {
    uis.add(webAppUI);
}

public List<WebAppUI> getUIs() {
    return uis;
}

public static UISession getInstance() {
    try {
        UI.getCurrent().getSession().lock();
        return (UISession) UI.getCurrent().getSession().getAttribute("userUiSession");
    } finally {
        UI.getCurrent().getSession().unlock();
    }
}

In UI.init() we add new instance to the session (e.g when user open new tab)

@Override
protected void init(VaadinRequest vaadinRequest) {
    /** Set singleton uisesison for each browser*/
    if(UISession.getInstance()==null){
        UI.getCurrent().getSession().setAttribute("userUiSession",new UISession());
    }
    UISession.getInstance().addUI(this);
    System.out.println("UI count fo current browser "+UISession.getInstance().getUIs().size());
    ... 
}

Here is my helper cookie class:

class MyCookie{
private String value;
private String name;
private Date expired;
private String path="/";

public MyCookie(String name, String value) {
    this.name=name;
    this.value=value;
}
public void setMaxAge(int minute) {
    Calendar c = Calendar.getInstance();
    c.add(Calendar.MINUTE, minute);
    expired=c.getTime();

}
public String getStringToCreateCookie(){
    return "document.cookie=\""+getName()+"="+getValue()+"; expires="+expired.toString()+"; path="+path+"\"";
}
public String getName() {
    return name;
}
public void setName(String name) {
    this.name = name;
}
public String getValue() {
    return value;
}
public void setValue(String value) {
    this.value = value;
}
public Date getExpired() {
    return expired;
}
public void setExpired(Date expired) {
    this.expired = expired;
}
public String getPath() {
    return path;
}
public void setPath(String path) {
    this.path = path;
}

}

And on final when we need add new cookie, we just must find Ui that is active and call js function

public static void addCookie(String name, String value, int age){
    MyCookie myCookie = new MyCookie(name, value);
    myCookie.setMaxAge(age);
    for(WebAppUI ui : UISession.getInstance().getUIs()){
        if(ui.isAttached()){
            ui.getPage().getJavaScript().execute(myCookie.getStringToCreateCookie());
            return;
        }
    }
}

In my case i have access to storage cookie (when user made request). I just only have problem with add new cookie so this is my working solutions.

Upvotes: 3

mstahv
mstahv

Reputation: 1934

The Viritin add-on contains a helper class called BrowserCookie. It works in pretty much the way suggested by cfrick, but just hides all the cookie handling complexity into a helper class. It don't contain built in "max age" handling yet, but that could be easily added as a workaround you can manually "encode" the age into cookie value.

BTW. Don't know what you are doing, but if you happen to be using TouchKit add-on, it has a helper for html5 local storage. It has rather wide browsers support already and is in many ways better way to store e.g. settings than cookies.

Upvotes: 0

cfrick
cfrick

Reputation: 37073

As mentioned in the ticket, you can use JavaScript to call client code and also request a cookie value back by that. E.g.

@Grapes([
        @Grab('org.vaadin.spring:spring-boot-vaadin:0.0.3'),
        @Grab('com.vaadin:vaadin-server:7.4.0.beta1'),
        @Grab('com.vaadin:vaadin-client-compiled:7.4.0.beta1'),
        @Grab('com.vaadin:vaadin-themes:7.4.0.beta1'),
        ])
import com.vaadin.ui.*

@org.vaadin.spring.VaadinUI
@groovy.transform.CompileStatic
class MyUI extends UI {

    protected void init(com.vaadin.server.VaadinRequest request) {
        final resultLabel = new Label()
        // provide a callback for the client to tell the cookies
        JavaScript.current.addFunction("tellCookie", { elemental.json.JsonArray arguments ->
            resultLabel.value = arguments?.get(0)?.asString()
        } as JavaScriptFunction)
        setContent(new VerticalLayout().with{
            addComponent(new Button("Set Cookie", {
                // just simply set the cookies via JS (attn: quoting etc)
                JavaScript.current.execute("document.cookie='mycookie=${System.currentTimeMillis()}'")
            } as Button.ClickListener))
            addComponent(new Button("Get Cookie", {
                // tell the client to tell the server the cookies
                JavaScript.current.execute("this.tellCookie(document.cookie)")
            } as Button.ClickListener))
            addComponent(resultLabel)
            return it
        })
    }
}

This is a running example (e.g. spring run vaadin.groovy) for testing. See the comments for the important parts.

Upvotes: 0

Related Questions