views:

16

answers:

1

I've tried to follow the facebook-java-api filter example but i got this expection when i attempted to save to the client object to the session. The code is being hosted on the google appengine platform.

java.lang.RuntimeException: java.io.NotSerializableException: com.google.code.facebookapi.FacebookXmlRestClient
    at com.google.apphosting.runtime.jetty.SessionManager.serialize(SessionManager.java:387)
    at com.google.apphosting.runtime.jetty.SessionManager.createEntityForSession(SessionManager.java:364)
    at com.google.apphosting.runtime.jetty.SessionManager$AppEngineSession.save(SessionManager.java:164)
    at com.google.apphosting.runtime.jetty.SaveSessionFilter.doFilter(SaveSessionFilter.java:41)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
    at com.google.apphosting.utils.servlet.TransactionCleanupFilter.doFilter(TransactionCleanupFilter.java:43)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
    at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:388)
    at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
    at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182)
    at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765)
    at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:418)
    .....
    at com.google.net.async.Connection.handleReadEvent(Connection.java:474)
    at com.google.net.async.EventDispatcher.processNetworkEvents(EventDispatcher.java:831)
    at com.google.net.async.EventDispatcher.internalLoop(EventDispatcher.java:207)
    at com.google.net.async.EventDispatcher.loop(EventDispatcher.java:103)
    at com.google.net.rpc.RpcService.runUntilServerShutdown(RpcService.java:251)
    at com.google.apphosting.runtime.JavaRuntime$RpcRunnable.run(JavaRuntime.java:418)
    at java.lang.Thread.run(Thread.java:636)
Caused by: java.io.NotSerializableException: com.google.code.facebookapi.FacebookXmlRestClient
    at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1173)
    at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:343)
    at java.util.HashMap.writeObject(HashMap.java:1018)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at  sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)

I updated the filter code to use a map to save the session and associated client object but i still can get a valid login. Can anybody spot the issue with this code, or explain how the com.google.code.facebookapi.ExtensibleClient can be used correctly?

import com.google.code.facebookapi.FacebookException;
import com.google.code.facebookapi.FacebookWebappHelper;
import com.google.code.facebookapi.FacebookXmlRestClient;
import com.google.code.facebookapi.IFacebookRestClient;

import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;

import org.w3c.dom.Document;

public class FacebookFilter implements Filter {

        private static final Logger logger = Logger.getLogger(FacebookFilter.class.getName());

        private String api_key;
        private String secret;
        private static Map map = new HashMap();

        public void init(FilterConfig filterConfig) throws ServletException {
                api_key = filterConfig.getInitParameter("facebook_api_key");
                secret = filterConfig.getInitParameter("facebook_secret_key");
                if(api_key == null || secret == null) {
                        throw new ServletException("Cannot initialise Facebook User Filter because the " +
                                                           "facebook_api_key or facebook_secret context init " +
                                                           "params have not been set. Check that they're there " +
                                                           "in your servlet context descriptor.");
                } else {
                        logger.info("Using facebook API key: " + api_key);
                }
        }

        public void destroy() {
        }

        public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
                try {
                        HttpServletRequest request = (HttpServletRequest)req;
                        HttpServletResponse response = (HttpServletResponse)res;

                        HttpSession session = request.getSession(true);
                        IFacebookRestClient<Document> userClient = getUserClient(session);
                        if(userClient == null) {
                            logger.info("User session doesn't have a Facebook API client setup yet. Creating one and storing it in the user's session.");
                            userClient = new FacebookXmlRestClient(api_key, secret);
                            logger.info("add new session to map "+map.size()+" "+session.getId()+" "+userClient.toString());
                            map.put(session,userClient);
                        }

                        logger.fine("Creating a FacebookWebappHelper, which copies fb_ request param data into the userClient");
                        FacebookWebappHelper<Document> facebook = new FacebookWebappHelper<Document>(request, response, api_key, secret, userClient);
                        String nextPage = request.getRequestURI();
                        nextPage = nextPage.substring(nextPage.indexOf("/", 1) + 1); //cut out the first /, the context path and the 2nd /
                        logger.fine(nextPage);
                        boolean redirectOccurred = facebook.requireLogin(nextPage);
                        if(redirectOccurred) {
                                return;
                        }
                        redirectOccurred = facebook.requireFrame(nextPage);
                        if(redirectOccurred) {
                                return;
                        }

                        long facebookUserID;
                        try {
                            facebookUserID = userClient.users_getLoggedInUser();
                            logger.info("facebookUserID "+facebookUserID+" "+session.getId());

                        } catch(FacebookException ex) {
                            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Error while fetching user's facebook ID");
                            logger.warning("Error while getting cached (supplied by request params) value " +
                                             "of the user's facebook ID or while fetching it from the Facebook service " +
                                             "if the cached value was not present for some reason. Cached value = {} "+ userClient.getCacheUserId());
                            return;
                        }
                        chain.doFilter(request, response);
                }
        }

    public static FacebookXmlRestClient getUserClient(HttpSession session) {
            if(map.containsKey(session))
            {
                logger.info("return match "+session.getId());
                return (FacebookXmlRestClient) map.get(session);
            }       
            else
            {
                logger.warning("getUserClient() null "+session.getId());
                return null;
            }

    }
}
A: 

Any objects that you put in an HttpSession will need to be marked as Serializable. (GAE uses the datastore to store session data)

Just wondering, instead of using the HttpSession as the key in the map, try using the session id? E.g.:

map.put(session.getId(),userClient);
Andrew Dyster
I'm not setting the userClient as a attribute or parameter of the http session or request object (to avoid the serializable issue) it is being added to a plain map.
emeraldjava
Is it coming up with the same error message when you try to put it in the map? Did you try my suggestion (using the session ID) ?
Andrew Dyster