Java tutorial
/* Copyright (C) 2013-2014 Ian Teune <ian.teune@gmail.com> * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package com.tinspx.util.net; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ListMultimap; import com.google.common.collect.Lists; import com.google.common.net.HttpHeaders; import com.tinspx.util.collect.CollectUtils; import java.io.IOException; import java.net.CookieHandler; import java.net.CookieManager; import java.net.CookiePolicy; import java.net.CookieStore; import java.net.HttpCookie; import java.net.URI; import java.util.Collections; import java.util.List; import java.util.Map; import javax.annotation.Nullable; /** * Cookie utility methods. * <p> * Important note: {@link CookieManager#get(URI, Map)} will always return a Map * with a "Cookie" key. If there were no cookies available, the value will be an * empty List. * * @author Ian */ public class Cookies { private Cookies() { } /** * The String "Cookie2" (without quotes) used as the Cookie2 header key */ public static final String COOKIE2 = "Cookie2"; private static final ImmutableMap<String, List<String>> EMPTY_GET_MAP = ImmutableMap.of(HttpHeaders.COOKIE, Collections.<String>emptyList()); private static final CookieHandler EMPTY_HANDLER = new CookieHandler() { @Override public ImmutableMap<String, List<String>> get(URI uri, Map<String, List<String>> requestHeaders) throws IOException { return EMPTY_GET_MAP; } @Override public void put(URI uri, Map<String, List<String>> responseHeaders) throws IOException { } }; /** * Returns a singleton empty {@link CookieHandler}. * {@link CookieHandler#put(URI, Map) put} has no effect and * {@link CookieHandler#get(URI, Map) get} always returns the empty Map. * * @return singleton empty {@link CookieHandler} */ public static CookieHandler emptyCookieHandler() { return EMPTY_HANDLER; } private static final CookieStore EMPTY_STORE = new CookieStore() { @Override public void add(URI uri, HttpCookie cookie) { } @Override public List<HttpCookie> get(URI uri) { return Collections.emptyList(); } @Override public List<HttpCookie> getCookies() { return Collections.emptyList(); } @Override public List<URI> getURIs() { return Collections.emptyList(); } @Override public boolean remove(URI uri, HttpCookie cookie) { return false; } @Override public boolean removeAll() { return false; } }; private static final CookieManager EMPTY_MANAGER = new CookieManager(EMPTY_STORE, CookiePolicy.ACCEPT_NONE); /** * Returns a singleton empty/blackhole {@link CookieStore} implementation. * Adding and removing cookies has no effect and all get methods return an * empty list. * * @return a singleton empty and unmodifiable {@link CookieStore} */ public static CookieStore emptyCookieStore() { return EMPTY_STORE; } /** * Returns a singleton empty {@link CookieManager} that uses * {@link #emptyCookieStore()} as the cookie store and * {@link CookiePolicy#ACCEPT_NONE} as the cookie policy. * * @return singleton empty and unmodifiable {@link CookieManager} */ public static CookieManager emptyCookieManager() { return EMPTY_MANAGER; } /** * Determines if the given CookieStore is null or equal to the singleton * empty CookiStore instance returned by {@link #emptyCookieStore()} * * @param store the CookieStore to check if null or empty * @return true if the CookieStore is null or equal to the CookieStore * returned from {@link #emptyCookieStore()} */ public static boolean isEmptyCookieStore(@Nullable CookieStore store) { return store == null || store == EMPTY_STORE; } /** * Returns true if the given CookieManager is null, equal to the singleton * CookieManager instance returned from {@link #emptyCookieManager()}, or * the manager's CookieStore is equal to the singleton empty CookiStore * instance returned by {@link #emptyCookieStore()} * * @param manager the CookieManager to check * @return true if the manager is null or represents an empty CookieManager */ public static boolean isEmptyCookieManager(@Nullable CookieManager manager) { return manager == null || manager == EMPTY_MANAGER || isEmptyCookieStore(manager.getCookieStore()); } /** * Adds cookie headers to {@code headers}. {@code header} is the name of the * header and should be "Cookie" or "Cookie2", but that is not a * requirement. {@code values} are the header values associated with * {@code header}. These values are added to {@code headers} as a * <i>single</i> entry. If there are multiple values in {@code values}, they * are combined into a single String delimited by the String "; " (semicolon * followed by space). Any values associated with the key {@code header} in * {@code headers} are replaced. * <p> * {@code values} may be null or empty. If this is the case, no changes are * made to {@code headers} and false is returned. If changes were made to * {@code headers} true is returned. * * @see #toSingleCookieList(java.util.List) * @param headers the headers ListMultimap to update * @param header the name of the header, will be the key used to update * {@code headers} * @param values the String values associated with {@code header} that will * be combined into a single entry * @return true if {@code headers} was modified */ public static boolean addFixed(ListMultimap<String, String> headers, String header, @Nullable List<String> values) { if (values == null || values.isEmpty()) { return false; } headers.replaceValues(header, CollectUtils.iterable(toSingleCookieString(values))); return true; } /** * Adds cookie headers to {@code headers}. {@code header} is the name of the * header and should be "Cookie" or "Cookie2", but that is not a * requirement. {@code values} are the header values associated with * {@code header}. These values are added to {@code headers} as a * single element List. If there are multiple values in {@code values}, they * are combined into a single String delimited by the String "; " (semicolon * followed by space). Any values associated with the key {@code header} in * {@code headers} are replaced. * <p> * {@code values} may be null or empty. If this is the case, no changes are * made to {@code headers} and false is returned. If changes were made to * {@code headers} true is returned. * * @see #toSingleCookieList(java.util.List) * @param headers the headers Map to update * @param header the name of the header, will be the key used to update * {@code headers} * @param values the String values associated with {@code header} that will * be combined into a single entry * @return true if {@code headers} was modified */ public static boolean addFixed(Map<String, List<String>> headers, String header, @Nullable List<String> values) { if (values == null || values.isEmpty()) { return false; } headers.put(header, Lists.newArrayList(toSingleCookieString(values))); return true; } /** * Fixes the Cookie and Cookie2 headers to ensure there is only one mapping * for each cookie header. Changes are made directly in the provided map. * <p> * If there are multiple mappings to either the Cookie or Cookie2 header, * they are combined into a single String delimited by the String "; " * (semicolon followed by space). * * @param headers the existing headers to fix * @return true if {@code headers} was modified */ public static boolean fixCookieHeaders(ListMultimap<String, String> headers) { boolean modified = false; List<String> h = headers.get(HttpHeaders.COOKIE); if (h.size() > 1) { headers.replaceValues(HttpHeaders.COOKIE, toSingleCookieList(h)); modified = true; } h = headers.get(COOKIE2); if (h.size() > 1) { headers.replaceValues(COOKIE2, toSingleCookieList(h)); modified = true; } return modified; } /** * Fixes the Cookie and Cookie2 headers to ensure there is only one mapping * for each cookie header. Changes are made directly in the provided map. * <p> * If there are multiple mappings to either the Cookie or Cookie2 header, * they are combined into a single String delimited by the String "; " * (semicolon followed by space). * * @param headers the existing headers to fix * @return true if {@code headers} was modified */ public static boolean fixCookieHeaders(Map<String, List<String>> headers) { boolean modified = false; List<String> h = headers.get(HttpHeaders.COOKIE); if (h != null && h.size() > 1) { headers.put(HttpHeaders.COOKIE, toSingleCookieList(h)); modified = true; } h = headers.get(COOKIE2); if (h != null && h.size() > 1) { headers.put(COOKIE2, toSingleCookieList(h)); modified = true; } return modified; } /** * "fixes" the list of cookies that are mapped to either Cookie or Cookie2 * headers. If {@code cookies} has more than one value, they are combined * into one String via {@link #toSingleCookieString(java.util.List)} and * and this combined string is set as the sole element of {@code cookies}. * * @param cookies the List of cookies to "fix", guaranteed to have at most * one element (could be empty} when this method returns * @return true if {@code cookies} was modified */ public static boolean fixCookieList(List<String> cookies) { if (cookies.size() > 1) { String cookie = toSingleCookieString(cookies); cookies.clear(); cookies.add(cookie); return true; } return false; } /** * Same behavior as {@link #fixCookieList(List)}, except a new modifiable * List instance is always created and returned, regardless if any changes * need to be made to {@code cookies}. The returned List will have at most * one element. If {@code cookies} is empty, a new empty List is returned. * If {@code cookies} has a single element, a new single element List is * returned with the same contents as {@code cookies}. If {@code cookies} * has more than one element, a new single element List is returned whose * sole element is the combinations of all the cookie Strings in * {@code cookies} delimited by "; " via * {@link #toSingleCookieString(java.util.List)}. The returned List is * always modifiable. * * @param cookies the list of cookies to fix and make a copy of, no * modifications are made to {@code cookies} * @return a new modifiable List that will at most one element, which will * be the combined cookie string */ public static List<String> toSingleCookieList(List<String> cookies) { if (cookies.size() <= 1) { return Lists.newArrayList(cookies); } else { return Lists.newArrayList(toSingleCookieString(cookies)); } } /** * Combines all Strings in {@code cookies} into single Cookie or Cookie2 * header value by combining them into a single String delimited by the * String "; " (semicolon followed by space). * <p> * If {@code cookies} is empty, the empty String is returned. If * {@code cookies} has a single element, the sole element is returned as is. * If {@code cookies} has more than one element, the Strings are appended in * the order they appear in the List, delimited by "; " (semicolon followed * by space). * * * @param cookies the List of cookies to combine * @return the combined cookie String, or the empty String if * {@code cookies} is empty */ public static String toSingleCookieString(List<String> cookies) { if (cookies.isEmpty()) { return ""; } else if (cookies.size() == 1) { return cookies.get(0); } int size = 0; for (String c : cookies) { if (c != null) { size += c.length() + 2; } } StringBuilder s = new StringBuilder(size); for (String c : cookies) { if (c != null) { if (s.length() > 0) { s.append("; "); } s.append(c); } } return s.toString(); } }