001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU General
007: * Public License Version 2 only ("GPL") or the Common Development and Distribution
008: * License("CDDL") (collectively, the "License"). You may not use this file except in
009: * compliance with the License. You can obtain a copy of the License at
010: * http://www.netbeans.org/cddl-gplv2.html or nbbuild/licenses/CDDL-GPL-2-CP. See the
011: * License for the specific language governing permissions and limitations under the
012: * License. When distributing the software, include this License Header Notice in
013: * each file and include the License file at nbbuild/licenses/CDDL-GPL-2-CP. Sun
014: * designates this particular file as subject to the "Classpath" exception as
015: * provided by Sun in the GPL Version 2 section of the License file that
016: * accompanied this code. If applicable, add the following below the License Header,
017: * with the fields enclosed by brackets [] replaced by your own identifying
018: * information: "Portions Copyrighted [year] [name of copyright owner]"
019: *
020: * Contributor(s):
021: *
022: * The Original Software is NetBeans. The Initial Developer of the Original Software
023: * is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun Microsystems, Inc. All
024: * Rights Reserved.
025: *
026: * If you wish your version of this file to be governed by only the CDDL or only the
027: * GPL Version 2, indicate your decision by adding "[Contributor] elects to include
028: * this software in this distribution under the [CDDL or GPL Version 2] license." If
029: * you do not indicate a single choice of license, a recipient has the option to
030: * distribute your version of this file under either the CDDL, the GPL Version 2 or
031: * to extend the choice of license to its licensees as provided above. However, if
032: * you add GPL Version 2 code and therefore, elected the GPL Version 2 license, then
033: * the option applies only if the new code is made subject to such option by the
034: * copyright holder.
035: */
036:
037: package org.netbeans.installer.downloader.connector;
038:
039: import java.io.File;
040: import java.io.IOException;
041: import java.net.*;
042: import java.util.ArrayList;
043: import java.util.List;
044: import org.netbeans.installer.downloader.DownloadManager;
045: import org.netbeans.installer.utils.ErrorManager;
046: import org.netbeans.installer.utils.LogManager;
047: import org.netbeans.installer.utils.ResourceUtils;
048: import org.netbeans.installer.utils.StringUtils;
049: import org.netbeans.installer.utils.exceptions.ParseException;
050: import org.netbeans.installer.utils.helper.Pair;
051: import org.netbeans.installer.utils.xml.DomUtil;
052: import org.netbeans.installer.utils.xml.visitors.DomVisitor;
053: import org.netbeans.installer.utils.xml.visitors.RecursiveDomVisitor;
054: import org.w3c.dom.Document;
055: import org.w3c.dom.Element;
056:
057: /**
058: *
059: * @author Danila_Dugurov
060: */
061:
062: public class URLConnector {
063: /////////////////////////////////////////////////////////////////////////////////
064: // Static
065: private static URLConnector instance; //Singleton
066:
067: public static synchronized URLConnector getConnector() {
068: if (instance != null) {
069: return instance;
070: }
071:
072: return instance = new URLConnector(new File(
073: DownloadManager.instance.getWd(), SETTINGS_FILE_NAME));
074: }
075:
076: /////////////////////////////////////////////////////////////////////////////////
077: // Instance
078: final MyProxySelector proxySelector = new MyProxySelector();
079:
080: int readTimeout = MINUTE / 3;
081: int connectTimeout = MINUTE / 3;
082: boolean doInput = true;
083: boolean doOutput = false;
084: boolean useCaches = false;
085:
086: boolean useProxy = false;
087:
088: private File settingFile;
089:
090: private void addSystemProxies() {
091: addProxyFrom(HTTP_PROXY_HOST_PROPERTY,
092: HTTPS_PROXY_PORT_PROPERTY, MyProxyType.HTTP);
093: addProxyFrom(FTP_PROXY_HOST_PROPERTY, FTP_PROXY_PORT_PROPERTY,
094: MyProxyType.FTP);
095: addProxyFrom(SOCKS_PROXY_HOST_PROPERTY,
096: SOCKS_PROXY_PORT_PROPERTY, MyProxyType.SOCKS);
097: }
098:
099: private void addDeploymentProxies() {
100: addProxyFrom(DEPLOYMENT_PROXY_HTTP_HOST,
101: DEPLOYMENT_PROXY_HTTP_PORT, MyProxyType.HTTP);
102: addProxyFrom(DEPLOYMENT_PROXY_FTP_HOST,
103: DEPLOYMENT_PROXY_FTP_PORT, MyProxyType.FTP);
104: addProxyFrom(DEPLOYMENT_PROXY_SOCKS_HOST,
105: DEPLOYMENT_PROXY_SOCKS_PORT, MyProxyType.SOCKS);
106:
107: if (DIRECT_CONNECTION_VALUE.equalsIgnoreCase(System
108: .getProperty(DIRECT_CONNECTION_PROPERTY))) {
109: useProxy = false;
110: }
111: }
112:
113: private void configureByPassList() {
114: addByPassHostsFrom(DEPLOYMENT_PROXY_BYPASS_LIST);
115: }
116:
117: private void addProxyFrom(final String hostProp,
118: final String portProp, final MyProxyType type) {
119: final String host = System.getProperty(hostProp);
120: final String stringPort = System.getProperty(portProp);
121: final int port = stringPort != null ? Integer
122: .parseInt(stringPort) : -1;
123:
124: if (host != null && port != -1) {
125: final Proxy socksProxy = new Proxy(type.getType(),
126: new InetSocketAddress(host, port));
127: proxySelector.add(new MyProxy(socksProxy, type));
128: useProxy = true;
129: }
130: }
131:
132: private void addByPassHostsFrom(final String byPassProp) {
133: final String byPassList = System.getProperty(byPassProp);
134: if (byPassList == null) {
135: return;
136: }
137: for (String host : byPassList
138: .split(PROXY_BYPASS_LIST_SEPARATOR)) {
139: host = host.trim();
140: if (!StringUtils.EMPTY_STRING.equals(host)) {
141: proxySelector.addByPassHost(host);
142: }
143: }
144: }
145:
146: public URLConnector(final File settingFile) {
147: addSystemProxies();
148: addDeploymentProxies();
149: configureByPassList();
150: this .settingFile = settingFile;
151:
152: if (settingFile.exists()) {
153: load();
154: LogManager.log("loaded configuration from file: "
155: + settingFile); // NOI18N
156: } else {
157: LogManager.log("" + settingFile + // NOI18N
158: " not exist, default configuration was set"); // NOI18N
159: }
160: }
161:
162: private void load() {
163: try {
164: Document settings = DomUtil.parseXmlFile(settingFile);
165: final DomVisitor visitor = new RecursiveDomVisitor() {
166: @Override
167: public void visit(Element elemet) {
168: if (READ_TIMEOUT_TAG.equals(elemet.getNodeName())) {
169: readTimeout = Integer.parseInt(elemet
170: .getTextContent().trim());
171: } else if (CONNECT_TIMEOUT_TAG.equals(elemet
172: .getNodeName())) {
173: connectTimeout = Integer.parseInt(elemet
174: .getTextContent().trim());
175: } else if (USE_PROXY_TAG.equals(elemet
176: .getNodeName())) {
177: useProxy = Boolean.valueOf(elemet
178: .getTextContent().trim());
179: } else if (PROXY_TAG.equals(elemet.getNodeName())) {
180: final MyProxy proxy = new MyProxy();
181: proxy.readXML(elemet);
182: proxySelector.add(proxy);
183: } else {
184: super .visit(elemet);
185: }
186: }
187: };
188:
189: visitor.visit(settings);
190: } catch (IOException e) {
191: ErrorManager.notifyDebug("I/O error during connector " + // NOI18N
192: "setting loading. Default configuration was set.",
193: e); // NOI18N
194: } catch (ParseException e) {
195: ErrorManager.notifyDebug("Failed to load settings: " + // NOI18N
196: "corrupted xml. Default configuration set.", e); // NOI18N
197: }
198: }
199:
200: public synchronized void dump() {
201: try {
202: final Document document = DomUtil
203: .parseXmlFile(DEFAULT_SETTINGS_TEXT);
204: final Element root = document.getDocumentElement();
205:
206: DomUtil.addElement(root, READ_TIMEOUT_TAG, String
207: .valueOf(readTimeout));
208: DomUtil.addElement(root, CONNECT_TIMEOUT_TAG, String
209: .valueOf(connectTimeout));
210: DomUtil.addElement(root, USE_PROXY_TAG, String
211: .valueOf(useProxy));
212: DomUtil.addChild(root, proxySelector);
213:
214: DomUtil.writeXmlFile(document, settingFile);
215: } catch (IOException e) {
216: ErrorManager.notifyDebug("I/O error. Can't " + // NOI18N
217: "dump configuration", e);
218: } catch (ParseException e) {
219: ErrorManager.notifyDebug("fatal error can't parse " + // NOI18N
220: "<connectSettings/>", e); // NOI18N
221: }
222: }
223:
224: public void addProxy(final MyProxy proxy) {
225: proxySelector.add(proxy);
226: dump();
227: }
228:
229: public void removeProxy(final MyProxyType type) {
230: proxySelector.remove(type);
231: dump();
232: }
233:
234: public void addByPassHost(final String host) {
235: proxySelector.addByPassHost(host);
236: }
237:
238: public void clearByPassList() {
239: proxySelector.clearByPassList();
240: }
241:
242: public String[] getByPassHosts() {
243: return proxySelector.getByPass();
244: }
245:
246: public void setReadTimeout(final int readTimeout) {
247: if (readTimeout < 0) {
248: throw new IllegalArgumentException();
249: }
250: this .readTimeout = readTimeout;
251: dump();
252: }
253:
254: public void setConnectTimeout(final int connectTimeout) {
255: if (connectTimeout < 0) {
256: throw new IllegalArgumentException();
257: }
258: this .connectTimeout = connectTimeout;
259: dump();
260: }
261:
262: public void setUseProxy(final boolean useProxy) {
263: this .useProxy = useProxy;
264:
265: dump();
266: }
267:
268: public int getReadTimeout() {
269: return readTimeout;
270: }
271:
272: public int getConnectTimeout() {
273: return connectTimeout;
274: }
275:
276: public boolean getUseProxy() {
277: return useProxy;
278: }
279:
280: public Proxy getProxy(final MyProxyType type) {
281: final MyProxy proxy = proxySelector.getForType(type);
282:
283: return (proxy != null) ? proxy.getProxy() : null;
284: }
285:
286: public URLConnection establishConnection(final URL url)
287: throws IOException {
288: return establishConnection(url,
289: new ArrayList<Pair<String, String>>(0));
290: }
291:
292: public URLConnection establishConnection(final URL url,
293: final List<Pair<String, String>> headerFields)
294: throws IOException {
295: Proxy proxy = null;
296: URI uri = null;
297: try {
298: proxy = useProxy ? proxySelector.select(uri = url.toURI())
299: .get(0) : Proxy.NO_PROXY;
300: final URLConnection connection = getConnectionThroughProxy(
301: url, proxy);
302: configure(connection, headerFields);
303: connection.connect();
304: return connection;
305: } catch (URISyntaxException e) {
306: ErrorManager.notifyDebug(url + " does not comply " + // NOI18N
307: "with RFC 2396 and cannot be converted to URI", e); // NOI18N
308:
309: return url.openConnection(Proxy.NO_PROXY);
310: } catch (IOException e) {
311: proxySelector.connectFailed(uri, proxy.address(), e);
312:
313: throw (IOException) new IOException(ResourceUtils
314: .getString(URLConnector.class,
315: ERROR_FAILED_PROXY_KEY, proxy, url))
316: .initCause(e);
317: }
318: }
319:
320: private URLConnection getConnectionThroughProxy(final URL url,
321: final Proxy proxy) throws IOException {
322: try {
323: return url.openConnection(proxy);
324: } catch (SecurityException e) {
325: ErrorManager.notifyDebug("No permission to connect to " + // NOI18N
326: "proxy: " + proxy, e); // NOI18N
327: } catch (IllegalArgumentException e) {
328: ErrorManager.notifyDebug("Proxy: " + proxy + "has wrong " + // NOI18N
329: "type.", e); // NOI18N
330: } catch (UnsupportedOperationException e) {
331: ErrorManager.notifyDebug(url.getProtocol()
332: + " handler does " + // NOI18N
333: "not support openConnection through proxy.", e); // NOI18N
334: }
335:
336: throw new IOException(ResourceUtils.getString(
337: URLConnector.class, ERROR_FAILED_PROXY_KEY, proxy, url));
338: }
339:
340: private void configure(final URLConnection connection,
341: final List<Pair<String, String>> headerFields) {
342: connection.setConnectTimeout(connectTimeout);
343: connection.setReadTimeout(readTimeout);
344: connection.setDoInput(doInput);
345: connection.setDoOutput(doOutput);
346: connection.setUseCaches(useCaches);
347: for (Pair<String, String> pair : headerFields) {
348: connection.setRequestProperty(pair.getFirst(), pair
349: .getSecond());
350: }
351: }
352:
353: /////////////////////////////////////////////////////////////////////////////////
354: // Constants
355: public static final int SECOND = 1000;
356:
357: public static final int MINUTE = 60 * SECOND;
358:
359: public static final String SETTINGS_FILE_NAME = "settings.xml"; // NOI18N
360:
361: public static final String HTTP_PROXY_HOST_PROPERTY = "http.proxyHost"; // NOI18N
362:
363: public static final String HTTP_PROXY_PORT_PROPERTY = "http.proxyPort"; // NOI18N
364:
365: public static final String FTP_PROXY_HOST_PROPERTY = "ftp.proxyHost"; // NOI18N
366:
367: public static final String FTP_PROXY_PORT_PROPERTY = "ftp.proxyPort"; // NOI18N
368:
369: public static final String HTTPS_PROXY_HOST_PROPERTY = "https.proxyHost"; // NOI18N
370:
371: public static final String HTTPS_PROXY_PORT_PROPERTY = "https.proxyPort"; // NOI18N
372:
373: public static final String SOCKS_PROXY_HOST_PROPERTY = "socksProxyHost"; // NOI18N
374:
375: public static final String SOCKS_PROXY_PORT_PROPERTY = "socksProxyPort"; // NOI18N
376:
377: public static final String DEPLOYMENT_PROXY_HTTP_HOST = "deployment.proxy.http.host"; // NOI18N
378:
379: public static final String DEPLOYMENT_PROXY_HTTP_PORT = "deployment.proxy.http.port"; // NOI18N
380:
381: public static final String DEPLOYMENT_PROXY_FTP_HOST = "deployment.proxy.ftp.host"; // NOI18N
382:
383: public static final String DEPLOYMENT_PROXY_FTP_PORT = "deployment.proxy.ftp.port"; // NOI18N
384:
385: public static final String DEPLOYMENT_PROXY_SOCKS_HOST = "deployment.proxy.socks.host"; // NOI18N
386:
387: public static final String DEPLOYMENT_PROXY_SOCKS_PORT = "deployment.proxy.socks.port"; // NOI18N
388:
389: public static final String DEPLOYMENT_PROXY_BYPASS_LIST = "deployment.proxy.bypass.list"; // NOI18N
390:
391: public static final String DIRECT_CONNECTION_PROPERTY = "javaplugin.proxy.config.type"; // NOI18N
392:
393: public static final String DIRECT_CONNECTION_VALUE = "direct"; // NOI18N
394:
395: public static final String PROXY_BYPASS_LIST_SEPARATOR = ",|;"; // NOI18N
396:
397: public static final String PROXY_TAG = "proxy"; // NOI18N
398:
399: public static final String USE_PROXY_TAG = "useProxy"; // NOI18N
400:
401: public static final String CONNECT_TIMEOUT_TAG = "connectTimeout"; // NOI18N
402:
403: public static final String READ_TIMEOUT_TAG = "readTimeout"; // NOI18N
404:
405: public static final String DEFAULT_SETTINGS_TEXT = "<connectSettings/>"; // NOI18N
406:
407: public static final String ERROR_FAILED_PROXY_KEY = "UC.error.failed.proxy"; // NOI18N
408: }
|