2

所以我的问题是我想在不同的容器(JFrame)中同时使用 2 个 javafx2.2.5 webview 实例。

我发现这个 hack 允许在不同的线程中使用不同的 cookiesstore。 如何使用 HttpURLConnection 和 Java 中的 CookieManager 为每个连接使用不同的 cookie 我无法使其工作,似乎 webview 创建了许多线程来加载页面,并且该线程请求默认的 cookie 处理程序,所以我最终得到 20不同的cookiestores(InMemoryStore)。此代码用于在javafx webview中自动登录ebay。

所以我为此使用的所有代码:

public class MySessionManager extends CookieHandler{
private final static MySessionManager ms_instance = new MySessionManager();
public static MySessionManager getInstance(){
  return ms_instance;
}

private final static ThreadLocal<CookieStore> ms_cookieJars = new ThreadLocal<CookieStore>() {
    @Override
    protected synchronized CookieStore initialValue() { return new InMemoryStore(); }
};

public void clear()
{
    getCookieStore().removeAll();
}

public CookieStore getCookieStore() {
   return ms_cookieJars.get();      
}    

private CookiePolicy policyCallback;    

public MySessionManager() {
    this(null, null);
}

public MySessionManager(CookieStore store,CookiePolicy cookiePolicy)   {
    // use default cookie policy if not specify one
    policyCallback = (cookiePolicy == null) ? CookiePolicy.ACCEPT_ORIGINAL_SERVER
                                            : cookiePolicy;
    // if not specify CookieStore to use, use default one
}

public void setCookiePolicy(CookiePolicy cookiePolicy) {
    if (cookiePolicy != null) policyCallback = cookiePolicy;
}
public Map<String, List<String>>
    get(URI uri, Map<String, List<String>> requestHeaders)
    throws IOException
{
    // pre-condition check
    if (uri == null || requestHeaders == null) {
        throw new IllegalArgumentException("Argument is null");
    }
    Map<String, List<String>> cookieMap =
                    new java.util.HashMap<String, List<String>>();
    // if there's no default CookieStore, no way for us to get any cookie
    if (getCookieStore() == null)
        return Collections.unmodifiableMap(cookieMap);

    boolean secureLink = "https".equalsIgnoreCase(uri.getScheme());
    List<HttpCookie> cookies = new java.util.ArrayList<HttpCookie>();
    String path = uri.getPath();
    if (path == null || path.isEmpty()) {
        path = "/";
    }
    for (HttpCookie cookie : getCookieStore().get(uri)) {
        // apply path-matches rule (RFC 2965 sec. 3.3.4)
        // and check for the possible "secure" tag (i.e. don't send
        // 'secure' cookies over unsecure links)
        if (pathMatches(path, cookie.getPath()) &&
                (secureLink || !cookie.getSecure())) {
            // Enforce httponly attribute
            if (cookie.isHttpOnly()) {
                String s = uri.getScheme();
                if (!"http".equalsIgnoreCase(s) && !"https".equalsIgnoreCase(s)) {
                    continue;
                }
            }
            // Let's check the authorize port list if it exists
            String ports = cookie.getPortlist();
            if (ports != null && !ports.isEmpty()) {
                int port = uri.getPort();
                if (port == -1) {
                    port = "https".equals(uri.getScheme()) ? 443 : 80;
                }
                if (isInPortList(ports, port)) {
                    cookies.add(cookie);
                }
            } else {
                cookies.add(cookie);
            }
        }
    }        // apply sort rule (RFC 2965 sec. 3.3.4)
    List<String> cookieHeader = sortByPath(cookies);

    cookieMap.put("Cookie", cookieHeader);
    return Collections.unmodifiableMap(cookieMap);
}
public void
    put(URI uri, Map<String, List<String>> responseHeaders)
    throws IOException
{
    // pre-condition check
    if (uri == null || responseHeaders == null) {
        throw new IllegalArgumentException("Argument is null");
    }
    // if there's no default CookieStore, no need to remember any cookie
    if (getCookieStore() == null)
        return;
//PlatformLogger logger = PlatformLogger.getLogger("java.net.MySessionManager");
    for (String headerKey : responseHeaders.keySet()) {
        // RFC 2965 3.2.2, key must be 'Set-Cookie2'
        // we also accept 'Set-Cookie' here for backward compatibility
        if (headerKey == null
            || !(headerKey.equalsIgnoreCase("Set-Cookie2")
                 || headerKey.equalsIgnoreCase("Set-Cookie")
                )
            )
        {
            continue;
        }

        for (String headerValue : responseHeaders.get(headerKey)) {
            try {
                List<HttpCookie> cookies;
                try {
                    cookies = HttpCookie.parse(headerValue);
                } catch (IllegalArgumentException e) {
                    // Bogus header, make an empty list and log the error
                    cookies = java.util.Collections.EMPTY_LIST;
                  /*  if (logger.isLoggable(PlatformLogger.SEVERE)) {
                        logger.severe("Invalid cookie for " + uri + ": " + headerValue);
                    }*/
                }
                for (HttpCookie cookie : cookies) {
                    if (cookie.getPath() == null) {
                        // If no path is specified, then by default
                        // the path is the directory of the page/doc
                        String path = uri.getPath();
                        if (!path.endsWith("/")) {
                            int i = path.lastIndexOf("/");
                            if (i > 0) {
                                path = path.substring(0, i + 1);
                            } else {
                                path = "/";
                            }
                        }
                        cookie.setPath(path);
                    }

                    // As per RFC 2965, section 3.3.1:
                    // Domain  Defaults to the effective request-host.  (Note that because
                    // there is no dot at the beginning of effective request-host,
                    // the default Domain can only domain-match itself.)
                    if (cookie.getDomain() == null) {
                        cookie.setDomain(uri.getHost());
                    }
                    String ports = cookie.getPortlist();
                    if (ports != null) {
                        int port = uri.getPort();
                        if (port == -1) {
                            port = "https".equals(uri.getScheme()) ? 443 : 80;
                        }
                        if (ports.isEmpty()) {
                            // Empty port list means this should be restricted
                            // to the incoming URI port
                            cookie.setPortlist("" + port );
                            if (shouldAcceptInternal(uri, cookie)) {
                                getCookieStore().add(uri, cookie);
                            }
                        } else {
                            // Only store cookies with a port list
                            // IF the URI port is in that list, as per
                            // RFC 2965 section 3.3.2
                            if (isInPortList(ports, port) &&
                                    shouldAcceptInternal(uri, cookie)) {
                                getCookieStore().add(uri, cookie);
                            }
                        }
                    } else {
                        if (shouldAcceptInternal(uri, cookie)) {
                            getCookieStore().add(uri, cookie);
                        }
                    }
                }
            } catch (IllegalArgumentException e) {
                // invalid set-cookie header string
                // no-op
            }
        }
    }
}
/* ---------------- Private operations -------------- */
// to determine whether or not accept this cookie
private boolean shouldAcceptInternal(URI uri, HttpCookie cookie) {
    try {
        return policyCallback.shouldAccept(uri, cookie);
    } catch (Exception ignored) { // pretect against malicious callback
        return false;
    }
}
static private boolean isInPortList(String lst, int port) {
    int i = lst.indexOf(",");
    int val = -1;
    while (i > 0) {
        try {
            val = Integer.parseInt(lst.substring(0, i));
            if (val == port) {
                return true;
            }
        } catch (NumberFormatException numberFormatException) {
        }
        lst = lst.substring(i+1);
        i = lst.indexOf(",");
    }
    if (!lst.isEmpty()) {
        try {
            val = Integer.parseInt(lst);
            if (val == port) {
                return true;
            }
        } catch (NumberFormatException numberFormatException) {
        }
    }
    return false;
}

/*
 * path-matches algorithm, as defined by RFC 2965
 */
private boolean pathMatches(String path, String pathToMatchWith) {
    if (path == pathToMatchWith)
        return true;
    if (path == null || pathToMatchWith == null)
        return false;
    if (path.startsWith(pathToMatchWith))
        return true;

    return false;
}
/*
 * sort cookies with respect to their path: those with more specific Path attributes
 * precede those with less specific, as defined in RFC 2965 sec. 3.3.4
 */
private List<String> sortByPath(List<HttpCookie> cookies) {
    Collections.sort(cookies, new MySessionManager.CookiePathComparator());

    List<String> cookieHeader = new java.util.ArrayList<String>();
    for (HttpCookie cookie : cookies) {
        // Netscape cookie spec and RFC 2965 have different format of Cookie
        // header; RFC 2965 requires a leading $Version="1" string while Netscape
        // does not.
        // The workaround here is to add a $Version="1" string in advance
        if (cookies.indexOf(cookie) == 0 && cookie.getVersion() > 0) {
            cookieHeader.add("$Version=\"1\"");
        }

        cookieHeader.add(cookie.toString());
    }
    return cookieHeader;
}
static class CookiePathComparator implements Comparator<HttpCookie> {
    public int compare(HttpCookie c1, HttpCookie c2) {
        if (c1 == c2) return 0;
        if (c1 == null) return -1;
        if (c2 == null) return 1;

        // path rule only applies to the cookies with same name
        if (!c1.getName().equals(c2.getName())) return 0;

        // those with more specific Path attributes precede those with less specific
        if (c1.getPath().startsWith(c2.getPath()))
            return -1;
        else if (c2.getPath().startsWith(c1.getPath()))
            return 1;
        else
            return 0;
    }
}    
}

CookieStore 实现:

  public class InMemoryStore  implements CookieStore {    
private static int count=0;
public int id;
// the in-memory representation of cookies
private List<HttpCookie> cookieJar = null;
// the cookies are indexed by its domain and associated uri (if present)
// CAUTION: when a cookie removed from main data structure (i.e. cookieJar),
//          it won't be cleared in domainIndex & uriIndex. Double-check the
//          presence of cookie when retrieve one form index store.
private Map<String, List<HttpCookie>> domainIndex = null;
private Map<URI, List<HttpCookie>> uriIndex = null;
// use ReentrantLock instead of syncronized for scalability
private ReentrantLock lock = null;
/**
 * The default ctor
 */
public InMemoryStore() {
    cookieJar = new ArrayList<HttpCookie>();
    domainIndex = new HashMap<String, List<HttpCookie>>();
    uriIndex = new HashMap<URI, List<HttpCookie>>();

    lock = new ReentrantLock(false);
    id=count;
    count++;
    System.out.println("New Memory Store "+id);
}
/**
 * Add one cookie into cookie store.
 */
public void add(URI uri, HttpCookie cookie) {
    // pre-condition : argument can't be null
    if (cookie == null) {
        throw new NullPointerException("cookie is null");
    }
    lock.lock();
    try {
        // remove the ole cookie if there has had one
        cookieJar.remove(cookie);
        // add new cookie if it has a non-zero max-age
        if (cookie.getMaxAge() != 0) {
            cookieJar.add(cookie);
            // and add it to domain index
            if (cookie.getDomain() != null) {
                addIndex(domainIndex, cookie.getDomain(), cookie);
            }
            // add it to uri index, too
            addIndex(uriIndex, getEffectiveURI(uri), cookie);
        }
    } finally {
        lock.unlock();
    }
}
/**
 * Get all cookies, which:
 *  1) given uri domain-matches with, or, associated with
 *     given uri when added to the cookie store.
 *  3) not expired.
 * See RFC 2965 sec. 3.3.4 for more detail.
 */
public List<HttpCookie> get(URI uri) {
    // argument can't be null
    if (uri == null) {
        throw new NullPointerException("uri is null");
    }
    List<HttpCookie> cookies = new ArrayList<HttpCookie>();
    boolean secureLink = "https".equalsIgnoreCase(uri.getScheme());
    lock.lock();
    try {
        // check domainIndex first
        getInternal1(cookies, domainIndex, uri.getHost(), secureLink);
        // check uriIndex then
        getInternal2(cookies, uriIndex, getEffectiveURI(uri), secureLink);
    } finally {
        lock.unlock();
    }
    return cookies;
}

/**
 * Get all cookies in cookie store, except those have expired
 */
public List<HttpCookie> getCookies() {
    List<HttpCookie> rt;

    lock.lock();
    try {
        Iterator<HttpCookie> it = cookieJar.iterator();
        while (it.hasNext()) {
            if (it.next().hasExpired()) {
                it.remove();
            }
        }
    } finally {
        rt = Collections.unmodifiableList(cookieJar);
        lock.unlock();
    }
    return rt;
}
/**
 * Get all URIs, which are associated with at least one cookie
 * of this cookie store.
 */
public List<URI> getURIs() {
    List<URI> uris = new ArrayList<URI>();
    lock.lock();
    try {
        Iterator<URI> it = uriIndex.keySet().iterator();
        while (it.hasNext()) {
            URI uri = it.next();
            List<HttpCookie> cookies = uriIndex.get(uri);
            if (cookies == null || cookies.size() == 0) {
                // no cookies list or an empty list associated with
                // this uri entry, delete it
                it.remove();
            }
        }
    } finally {
        uris.addAll(uriIndex.keySet());
        lock.unlock();
    }
    return uris;
}
/**
 * Remove a cookie from store
 */
public boolean remove(URI uri, HttpCookie ck) {
    // argument can't be null
    if (ck == null) {
        throw new NullPointerException("cookie is null");
    }
    boolean modified = false;
    lock.lock();
    try {
        modified = cookieJar.remove(ck);
    } finally {
        lock.unlock();
    }

    return modified;
}
/**
 * Remove all cookies in this cookie store.
 */
public boolean removeAll() {
    lock.lock();
    try {
        cookieJar.clear();
        domainIndex.clear();
        uriIndex.clear();
    } finally {
        lock.unlock();
    }
    return true;
}
/* ---------------- Private operations -------------- */
/*
 * This is almost the same as HttpCookie.domainMatches except for
 * one difference: It won't reject cookies when the 'H' part of the
 * domain contains a dot ('.').
 * I.E.: RFC 2965 section 3.3.2 says that if host is x.y.domain.com
 * and the cookie domain is .domain.com, then it should be rejected.
 * However that's not how the real world works. Browsers don't reject and
 * some sites, like yahoo.com do actually expect these cookies to be
 * passed along.
 * And should be used for 'old' style cookies (aka Netscape type of cookies)
 */
private boolean netscapeDomainMatches(String domain, String host)
{
    if (domain == null || host == null) {
        return false;
    }
    // if there's no embedded dot in domain and domain is not .local
    boolean isLocalDomain = ".local".equalsIgnoreCase(domain);
    int embeddedDotInDomain = domain.indexOf('.');
    if (embeddedDotInDomain == 0) {
        embeddedDotInDomain = domain.indexOf('.', 1);
    }
    if (!isLocalDomain && (embeddedDotInDomain == -1 || embeddedDotInDomain == domain.length() - 1)) {
        return false;
    }
    // if the host name contains no dot and the domain name is .local
    int firstDotInHost = host.indexOf('.');
    if (firstDotInHost == -1 && isLocalDomain) {
        return true;
    }
    int domainLength = domain.length();
    int lengthDiff = host.length() - domainLength;
    if (lengthDiff == 0) {
        // if the host name and the domain name are just string-compare euqal
        return host.equalsIgnoreCase(domain);
    } else if (lengthDiff > 0) {
        // need to check H & D component
        String H = host.substring(0, lengthDiff);
        String D = host.substring(lengthDiff);

        return (D.equalsIgnoreCase(domain));
    } else if (lengthDiff == -1) {
        // if domain is actually .host
        return (domain.charAt(0) == '.' &&
                host.equalsIgnoreCase(domain.substring(1)));
    }
    return false;
}
private void getInternal1(List<HttpCookie> cookies, Map<String, List<HttpCookie>> cookieIndex,
        String host, boolean secureLink) {
    // Use a separate list to handle cookies that need to be removed so
    // that there is no conflict with iterators.
    ArrayList<HttpCookie> toRemove = new ArrayList<HttpCookie>();
    for (Map.Entry<String, List<HttpCookie>> entry : cookieIndex.entrySet()) {
        String domain = entry.getKey();
        List<HttpCookie> lst = entry.getValue();
        for (HttpCookie c : lst) {
            if ((c.getVersion() == 0 && netscapeDomainMatches(domain, host)) ||
                    (c.getVersion() == 1 && HttpCookie.domainMatches(domain, host))) {
                if ((cookieJar.indexOf(c) != -1)) {
                    // the cookie still in main cookie store
                    if (!c.hasExpired()) {
                        // don't add twice and make sure it's the proper
                        // security level
                        if ((secureLink || !c.getSecure()) &&
                                !cookies.contains(c)) {
                            cookies.add(c);
                        }
                    } else {
                        toRemove.add(c);
                    }
                } else {
                    // the cookie has beed removed from main store,
                    // so also remove it from domain indexed store
                    toRemove.add(c);
                }
            }
        }
        // Clear up the cookies that need to be removed
        for (HttpCookie c : toRemove) {
            lst.remove(c);
            cookieJar.remove(c);
        }
        toRemove.clear();
    }
}
// @param cookies           [OUT] contains the found cookies
// @param cookieIndex       the index
// @param comparator        the prediction to decide whether or not
//                          a cookie in index should be returned
private <T> void getInternal2(List<HttpCookie> cookies,
                            Map<T, List<HttpCookie>> cookieIndex,
                            Comparable<T> comparator, boolean secureLink)
{
    for (T index : cookieIndex.keySet()) {
        if (comparator.compareTo(index) == 0) {
            List<HttpCookie> indexedCookies = cookieIndex.get(index);
            // check the list of cookies associated with this domain
            if (indexedCookies != null) {
                Iterator<HttpCookie> it = indexedCookies.iterator();
                while (it.hasNext()) {
                    HttpCookie ck = it.next();
                    if (cookieJar.indexOf(ck) != -1) {
                        // the cookie still in main cookie store
                        if (!ck.hasExpired()) {
                            // don't add twice
                            if ((secureLink || !ck.getSecure()) &&
                                    !cookies.contains(ck))
                                cookies.add(ck);
                        } else {
                            it.remove();
                            cookieJar.remove(ck);
                        }
                    } else {
                        // the cookie has beed removed from main store,
                        // so also remove it from domain indexed store
                        it.remove();
                    }
                }
            } // end of indexedCookies != null
        } // end of comparator.compareTo(index) == 0
    } // end of cookieIndex iteration
}
// add 'cookie' indexed by 'index' into 'indexStore'
private <T> void addIndex(Map<T, List<HttpCookie>> indexStore,
                          T index,
                          HttpCookie cookie)
{
    if (index != null) {
        List<HttpCookie> cookies = indexStore.get(index);
        if (cookies != null) {
            // there may already have the same cookie, so remove it first
            cookies.remove(cookie);
            cookies.add(cookie);
        } else {
            cookies = new ArrayList<HttpCookie>();
            cookies.add(cookie);
            indexStore.put(index, cookies);
        }
    }
}
//
// for cookie purpose, the effective uri should only be http://host
// the path will be taken into account when path-match algorithm applied
//
private URI getEffectiveURI(URI uri) {
    URI effectiveURI = null;
    try {
        effectiveURI = new URI("http",
                               uri.getHost(),
                               null,  // path component
                               null,  // query component
                               null   // fragment component
                              );
    } catch (URISyntaxException ignored) {
        effectiveURI = uri;
    }
    return effectiveURI;
}
}

测试浏览器:

public class TestBrowsers extends javax.swing.JFrame { 
private static final String ebaySignInUrl="https://signin.ebay.co.uk/ws/eBayISAPI.dll?SignIn";      
private JFXPanel fxPanel;
private WebEngine webEngine;          
private String username;
private String pass;    
private boolean loginPage =false;  
public TestBrowsers(String username,String pass) {
    initComponents();
    System.out.println("Threads "+Thread.activeCount());
    System.out.println("Thread "+Thread.currentThread().toString());       
    this.username=username;
    this.pass=pass;      
    initComponents();       
    fxPanel = new JFXPanel();         
    Platform.runLater(new Runnable() {           
        @Override
        public void run() {
            initFxComponents();            
        }
   });             
   this.setSize(850, 650);
}    
  private void initFxComponents(){
      // This method is invoked on the JavaFX thread
      createScene();            
}

private void createScene(){        
    final StackPane root = new StackPane();
    CookieHandler.setDefault(MySessionManager.getInstance());
    final WebView browser = new WebView();   
    System.out.println("Thread count:: "+Thread.activeCount());        
    webEngine = browser.getEngine();    
    webEngine.load(ebaySignInUrl);
    final Scene scene = new Scene(root, 500, 400);
   // System.out.println("Browser loading..");
    //make visible
    setScene(scene);
    root.getChildren().add(browser);
    webEngine.getLoadWorker().stateProperty().addListener(new ChangeListener() {

        @Override
        public void changed(ObservableValue ov, Object t, Object t1) {
            setTitle(webEngine.getLocation());
            Worker.State state = (Worker.State)t1;
            if(state==Worker.State.SUCCEEDED){                
                 if(!loginPage){
                     System.out.println("Login in.. "+webEngine.getLocation());                 
                         Document doc = webEngine.getDocument();
                         Element u = (Element) doc.getElementById("userid");
                         u.setAttribute("value", username);
                         Element p = (Element)doc.getElementById("pass");
                         p.setAttribute("value", pass);

                  try{
                     webEngine.executeScript("document.getElementById('sgnBt').click()");
                   loginPage=true;
                   //show threads
                      System.out.println(Thread.currentThread().getName());
                      System.out.println(Thread.currentThread().getId());
                      }catch(Exception ex){
                          ex.printStackTrace();                          
                     }                   
                 }

            }
        } 
     });


}   

private void setScene(Scene scene){
    System.out.println("Displaying..");  
    GridBagConstraints gbc = new GridBagConstraints();
    gbc.fill=GridBagConstraints.BOTH;
    gbc.weightx=1.0;
    gbc.weighty=1.0;      
    fxPanel.setScene(scene);       
    this.add(fxPanel,gbc);
    this.revalidate();
    this.setTitle(webEngine.getLocation());    
}   
 private void initComponents() {
    setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
    getContentPane().setLayout(new java.awt.GridBagLayout());        pack();
} 
public static void main(String args[]) {     
   java.awt.EventQueue.invokeLater(new Runnable() {
        public void run() {              
            new TestBrowsers("username","password").setVisible(true);             
        }
    });
}

}
4

0 回答 0