View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   * http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  /*
20   * This class has been taken from Apache Harmony (http://harmony.apache.org/) 
21   * and has been modified to work with OpenCMIS.
22   */
23  package org.apache.chemistry.opencmis.client.bindings.spi.cookies;
24  
25  import java.io.Serializable;
26  import java.net.URI;
27  import java.net.URISyntaxException;
28  import java.util.ArrayList;
29  import java.util.Collections;
30  import java.util.List;
31  import java.util.Map;
32  import java.util.concurrent.locks.ReentrantReadWriteLock;
33  
34  import org.apache.chemistry.opencmis.commons.exceptions.CmisConnectionException;
35  import org.slf4j.Logger;
36  import org.slf4j.LoggerFactory;
37  
38  /**
39   * Cookie Manager.
40   * 
41   * This implementation conforms to RFC 2965, section 3.3 with some RFC 6265
42   * extensions.
43   */
44  public class CmisCookieManager implements Serializable {
45      private static final long serialVersionUID = 1L;
46  
47      private static final Logger LOG = LoggerFactory.getLogger(CmisCookieManager.class.getName());
48  
49      private static final String VERSION_ZERO_HEADER = "Set-cookie";
50      private static final String VERSION_ONE_HEADER = "Set-cookie2";
51  
52      private final String sessionId;
53      private final CmisCookieStoreImpl store;
54      private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
55  
56      /**
57       * Constructs a new cookie manager.
58       */
59      public CmisCookieManager() {
60          this("<unknown>");
61      }
62  
63      /**
64       * Constructs a new cookie manager.
65       */
66      public CmisCookieManager(String sessionId) {
67          this.sessionId = sessionId;
68          store = new CmisCookieStoreImpl();
69      }
70  
71      /**
72       * Searches and gets all cookies in the cache by the specified URL in the
73       * request header.
74       * 
75       * @param url
76       *            the specified URL to search for
77       * @param requestHeaders
78       *            a list of request headers
79       * @return a map that record all such cookies, the map is unchangeable
80       */
81      public Map<String, List<String>> get(String url, Map<String, List<String>> requestHeaders) {
82          if (url == null || requestHeaders == null) {
83              throw new IllegalArgumentException("URL or headers are null!");
84          }
85  
86          URI uri;
87          try {
88              uri = new URI(url);
89          } catch (URISyntaxException e) {
90              throw new CmisConnectionException(e.getMessage(), e);
91          }
92  
93          lock.writeLock().lock();
94          try {
95              List<CmisHttpCookie> cookies = store.get(uri);
96              String uriPath = uri.getPath();
97              for (int i = 0; i < cookies.size(); i++) {
98                  CmisHttpCookie cookie = cookies.get(i);
99                  String cookiePath = cookie.getPath();
100                 // if the uri's path does not path-match cookie's path, remove
101                 // cookies from the list
102                 if (cookiePath == null || uriPath.length() == 0 || !uriPath.startsWith(cookiePath)) {
103                     cookies.remove(i);
104                 }
105             }
106 
107             Map<String, List<String>> map = getCookieMap(cookies, requestHeaders);
108 
109             if (LOG.isDebugEnabled()) {
110                 if (map != null && !map.isEmpty()) {
111                     LOG.debug("Session {}: Setting cookies for URL {}: {}", sessionId, url,
112                             map.get("Cookie") == null ? "" : map.get("Cookie").toString());
113                 }
114             }
115 
116             return map;
117         } finally {
118             lock.writeLock().unlock();
119         }
120     }
121 
122     private static Map<String, List<String>> getCookieMap(List<CmisHttpCookie> cookies,
123             Map<String, List<String>> requestHeaders) {
124         if (cookies.isEmpty()) {
125             return Collections.emptyMap();
126         }
127 
128         StringBuilder cookieHeaderStr = new StringBuilder(128);
129 
130         for (CmisHttpCookie cookie : cookies) {
131             if (cookieHeaderStr.length() > 0) {
132                 cookieHeaderStr.append("; ");
133             }
134             cookieHeaderStr.append(cookie.getName());
135             cookieHeaderStr.append('=');
136             cookieHeaderStr.append(cookie.getValue());
137         }
138 
139         return Collections.singletonMap("Cookie", Collections.singletonList(cookieHeaderStr.toString()));
140     }
141 
142     /**
143      * Sets cookies according to URL and responseHeaders
144      * 
145      * @param url
146      *            the specified URL
147      * @param responseHeaders
148      *            a list of request headers
149      */
150     public void put(String url, Map<String, List<String>> responseHeaders) {
151         if (url == null || responseHeaders == null) {
152             throw new IllegalArgumentException("URL or headers are null!");
153         }
154 
155         URI uri;
156         try {
157             uri = new URI(url);
158         } catch (URISyntaxException e) {
159             throw new CmisConnectionException(e.getMessage(), e);
160         }
161 
162         lock.writeLock().lock();
163         try {
164             // parse and construct cookies according to the map
165             List<CmisHttpCookie> cookies = parseCookie(responseHeaders);
166             for (CmisHttpCookie cookie : cookies) {
167                 if (cookie.getDomain() == null) {
168                     cookie.setDomain(uri.getHost());
169                 }
170                 if (cookie.getPath() == null) {
171                     cookie.setPath("/");
172                 }
173                 store.add(uri, cookie);
174             }
175 
176             if (LOG.isDebugEnabled()) {
177                 if (!cookies.isEmpty()) {
178                     LOG.debug("Session {}: Retrieved cookies for URL {}: {}", sessionId, url, cookies.toString());
179                 }
180             }
181         } finally {
182             lock.writeLock().unlock();
183         }
184     }
185 
186     /**
187      * Removes all cookies.
188      */
189     public void clear() {
190         lock.writeLock().lock();
191         try {
192             store.clear();
193         } finally {
194             lock.writeLock().unlock();
195         }
196     }
197 
198     private static List<CmisHttpCookie> parseCookie(Map<String, List<String>> responseHeaders) {
199         List<CmisHttpCookie> cookies = new ArrayList<CmisHttpCookie>();
200         for (Map.Entry<String, List<String>> entry : responseHeaders.entrySet()) {
201             String key = entry.getKey();
202             // Only "Set-cookie" and "Set-cookie2" pair will be parsed
203             if (key != null && (key.equalsIgnoreCase(VERSION_ZERO_HEADER) || key.equalsIgnoreCase(VERSION_ONE_HEADER))) {
204                 // parse list elements one by one
205                 for (String cookieStr : entry.getValue()) {
206                     try {
207                         for (CmisHttpCookie cookie : CmisHttpCookie.parse(cookieStr)) {
208                             cookies.add(cookie);
209                         }
210                     } catch (IllegalArgumentException e) {
211                         // this string is invalid, jump to the next one.
212                     }
213                 }
214             }
215         }
216 
217         return cookies;
218     }
219 
220     /**
221      * Gets current cookie store.
222      * 
223      * @return the cookie store currently used by cookie manager.
224      */
225     public CmisCookieStoreImpl getCookieStore() {
226         return store;
227     }
228 }