PageRenderTime 55ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 1ms

/projects/netbeans-7.3/httpserver/src/org/netbeans/modules/httpserver/HttpServerSettings.java

https://gitlab.com/essere.lab.public/qualitas.class-corpus
Java | 578 lines | 373 code | 79 blank | 126 comment | 64 complexity | c6b9b58f41f8f2479d31230778089180 MD5 | raw file
  1. /*
  2. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  3. *
  4. * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved.
  5. *
  6. * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
  7. * Other names may be trademarks of their respective owners.
  8. *
  9. * The contents of this file are subject to the terms of either the GNU
  10. * General Public License Version 2 only ("GPL") or the Common
  11. * Development and Distribution License("CDDL") (collectively, the
  12. * "License"). You may not use this file except in compliance with the
  13. * License. You can obtain a copy of the License at
  14. * http://www.netbeans.org/cddl-gplv2.html
  15. * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
  16. * specific language governing permissions and limitations under the
  17. * License. When distributing the software, include this License Header
  18. * Notice in each file and include the License file at
  19. * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
  20. * particular file as subject to the "Classpath" exception as provided
  21. * by Oracle in the GPL Version 2 section of the License file that
  22. * accompanied this code. If applicable, add the following below the
  23. * License Header, with the fields enclosed by brackets [] replaced by
  24. * your own identifying information:
  25. * "Portions Copyrighted [year] [name of copyright owner]"
  26. *
  27. * Contributor(s):
  28. *
  29. * The Original Software is NetBeans. The Initial Developer of the Original
  30. * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
  31. * Microsystems, Inc. All Rights Reserved.
  32. *
  33. * If you wish your version of this file to be governed by only the CDDL
  34. * or only the GPL Version 2, indicate your decision by adding
  35. * "[Contributor] elects to include this software in this distribution
  36. * under the [CDDL or GPL Version 2] license." If you do not indicate a
  37. * single choice of license, a recipient has the option to distribute
  38. * your version of this file under either the CDDL, the GPL Version 2 or
  39. * to extend the choice of license to its licensees as provided above.
  40. * However, if you add GPL Version 2 code and therefore, elected the GPL
  41. * Version 2 license, then the option applies only if the new code is
  42. * made subject to such option by the copyright holder.
  43. */
  44. package org.netbeans.modules.httpserver;
  45. import java.awt.Dialog;
  46. import java.util.Hashtable;
  47. import java.util.HashSet;
  48. import java.util.Set;
  49. import java.util.StringTokenizer;
  50. import java.util.Properties;
  51. import java.net.InetAddress;
  52. import java.net.UnknownHostException;
  53. import java.util.prefs.Preferences;
  54. import javax.swing.event.EventListenerList;
  55. import org.openide.DialogDescriptor;
  56. import org.openide.util.NbBundle;
  57. import org.openide.util.HelpCtx;
  58. import org.openide.util.Utilities;
  59. import org.openide.NotifyDescriptor;
  60. import org.openide.DialogDisplayer;
  61. import org.openide.nodes.BeanNode;
  62. import org.openide.util.NbPreferences;
  63. /** Options for http server
  64. *
  65. * @author Ales Novak, Petr Jiricka
  66. */
  67. public class HttpServerSettings {
  68. private static HttpServerSettings INSTANCE = new HttpServerSettings();
  69. private static BeanNode view = null;
  70. private static final int MAX_START_RETRIES = 20;
  71. private static int currentRetries = 0;
  72. protected static EventListenerList listenerList = new EventListenerList();
  73. /** Has this been initialized ?
  74. * Becomes true if a "running" getter or setter is called
  75. */
  76. static boolean inited = false;
  77. /** Contains threads which are or will be asking for access for the given IP address. */
  78. private static Hashtable<InetAddress,Thread> whoAsking = new Hashtable<InetAddress,Thread>();
  79. public static final int SERVER_STARTUP_TIMEOUT = 3000;
  80. /** constant for local host */
  81. public static final String LOCALHOST = "local"; // NOI18N
  82. /** constant for any host */
  83. public static final String ANYHOST = "any"; // NOI18N
  84. public static HostProperty hostProperty = null;
  85. public static final String PROP_PORT = "port"; // NOI18N
  86. public static final String PROP_HOST_PROPERTY = "hostProperty"; // NOI18N
  87. static final String PROP_WRAPPER_BASEURL = "wrapperBaseURL"; // NOI18N
  88. public static final String PROP_RUNNING = "running"; // NOI18N
  89. private static final String PROP_SHOW_GRANT_ACCESS = "showGrantAccess"; // NOI18N
  90. /** port */
  91. private static final int DEFAULT_PORT = 8082;
  92. /** mapping of wrapper to URL */
  93. private static String wrapperBaseURL = "/resource/"; // NOI18N
  94. /** Reflects whether the server is actually running, not the running property */
  95. static boolean running = false;
  96. private static boolean startStopMessages = true;
  97. private static Properties mappedServlets = new Properties();
  98. /** http settings
  99. * @deprecated use <CODE>SharedClassObject.findObject()</CODE>
  100. */
  101. public static HttpServerSettings OPTIONS = null;
  102. /** Lock for the httpserver operations */
  103. private static Object httpLock;
  104. private static Preferences getPreferences() {
  105. return NbPreferences.forModule(HttpServerSettings.class);
  106. }
  107. /**
  108. * Obtains lock for httpserver synchronization
  109. */
  110. static final Object httpLock () {
  111. if (httpLock == null) {
  112. httpLock = new Object ();
  113. }
  114. return httpLock;
  115. }
  116. private HttpServerSettings() {
  117. }
  118. public static HttpServerSettings getDefault() {
  119. return INSTANCE;
  120. }
  121. /** getter for running status */
  122. public boolean isRunning() {
  123. if (inited) {
  124. return running;
  125. }
  126. else {
  127. // this used to be true, but it seems more reasonable not to start the server by default
  128. // Fixes bug 11347
  129. setRunning(false);
  130. return running;
  131. }
  132. }
  133. /** Intended to be called by the thread which succeeded to start the server */
  134. void runSuccess() {
  135. synchronized (httpLock ()) {
  136. currentRetries = 0;
  137. running = true;
  138. httpLock ().notifyAll();
  139. }
  140. }
  141. /** Intended to be called by the thread which failed to start the server.
  142. * It decides whether try to start server on next port or show appropriate
  143. * error message.
  144. */
  145. void runFailure(Throwable t) {
  146. running = false;
  147. if (t instanceof IncompatibleClassChangeError) {
  148. // likely there is a wrong servlet API version on CLASSPATH
  149. DialogDisplayer.getDefault ().notify(new NotifyDescriptor.Message(
  150. NbBundle.getMessage (HttpServerSettings.class, "MSG_HTTP_SERVER_incompatbleClasses"),
  151. NotifyDescriptor.Message.WARNING_MESSAGE));
  152. }
  153. else if (t instanceof java.net.BindException) {
  154. // can't open socket - we can retry
  155. currentRetries ++;
  156. if (currentRetries <= MAX_START_RETRIES) {
  157. setPort(getPort() + 1);
  158. setRunning(true);
  159. }
  160. else {
  161. currentRetries = 0;
  162. DialogDisplayer.getDefault ().notify(new NotifyDescriptor.Message(
  163. NbBundle.getMessage (HttpServerSettings.class, "MSG_HTTP_SERVER_START_FAIL"),
  164. NotifyDescriptor.Message.WARNING_MESSAGE));
  165. int p = getPort ();
  166. if (p < 1024 && inited && Utilities.isUnix()) {
  167. DialogDisplayer.getDefault ().notify(new NotifyDescriptor.Message(
  168. NbBundle.getMessage (HttpServerSettings.class, "MSG_onlyRootOnUnix"),
  169. NotifyDescriptor.WARNING_MESSAGE));
  170. }
  171. }
  172. }
  173. else {
  174. // unknown problem
  175. DialogDisplayer.getDefault ().notify(new NotifyDescriptor.Message(
  176. NbBundle.getMessage (HttpServerSettings.class, "MSG_HTTP_SERVER_START_FAIL_unknown"),
  177. NotifyDescriptor.Message.WARNING_MESSAGE));
  178. }
  179. }
  180. /** Restarts the server if it is running - must be called in a synchronized block
  181. * No need to restart if it is called during deserialization.
  182. */
  183. private void restartIfNecessary(boolean printMessages) {
  184. if (running) {
  185. if (!printMessages)
  186. setStartStopMessages(false);
  187. HttpServerModule.stopHTTPServer();
  188. HttpServerModule.initHTTPServer();
  189. // messages will be enabled by the server thread
  190. }
  191. }
  192. /** Returns a relative directory URL with a leading and a trailing slash */
  193. private String getCanonicalRelativeURL(String url) {
  194. String newURL;
  195. if (url.length() == 0)
  196. newURL = "/"; // NOI18N
  197. else {
  198. if (url.charAt(0) != '/')
  199. newURL = "/" + url; // NOI18N
  200. else
  201. newURL = url;
  202. if (newURL.charAt(newURL.length() - 1) != '/')
  203. newURL = newURL + "/"; // NOI18N
  204. }
  205. return newURL;
  206. }
  207. /** setter for running status */
  208. public void setRunning(boolean running) {
  209. inited = true;
  210. if (this.running == running)
  211. return;
  212. synchronized (httpLock ()) {
  213. if (running) {
  214. // running status is set by another thread
  215. HttpServerModule.initHTTPServer();
  216. }
  217. else {
  218. this.running = false;
  219. HttpServerModule.stopHTTPServer();
  220. }
  221. }
  222. }
  223. // NOT publicly available
  224. /** getter for classpath base */
  225. String getWrapperBaseURL() {
  226. return wrapperBaseURL;
  227. }
  228. /** setter for classpath base */
  229. void setWrapperBaseURL(String wrapperBaseURL) {
  230. // canonical form starts and ends with a /
  231. String oldURL;
  232. String newURL = getCanonicalRelativeURL(wrapperBaseURL);
  233. // check if any change is taking place
  234. if (this.wrapperBaseURL.equals(newURL))
  235. return;
  236. // implement the change
  237. synchronized (httpLock ()) {
  238. oldURL = this.wrapperBaseURL;
  239. this.wrapperBaseURL = newURL;
  240. restartIfNecessary(false);
  241. }
  242. }
  243. /** setter for port */
  244. public void setPort(int p) {
  245. if (p <= 0 || p >65535) {
  246. NotifyDescriptor.Message msg = new NotifyDescriptor.Message(
  247. NbBundle.getMessage(HttpServerSettings.class, "ERR_PortNumberOutOfRange", new Integer(p)), NotifyDescriptor.ERROR_MESSAGE);
  248. DialogDisplayer.getDefault().notify(msg);
  249. return;
  250. }
  251. synchronized (httpLock ()) {
  252. getPreferences().putInt(PROP_PORT,p);
  253. restartIfNecessary(true);
  254. }
  255. }
  256. /** getter for port */
  257. public int getPort() {
  258. return getPreferences().getInt(PROP_PORT, DEFAULT_PORT);
  259. }
  260. public void setStartStopMessages(boolean ssm) {
  261. startStopMessages = ssm;
  262. }
  263. public boolean isStartStopMessages() {
  264. return startStopMessages;
  265. }
  266. public HelpCtx getHelpCtx () {
  267. return new HelpCtx (HttpServerSettings.class);
  268. }
  269. /** Returns string for localhost */
  270. private String getLocalHost() {
  271. try {
  272. return InetAddress.getLocalHost().getHostName();
  273. }
  274. catch (UnknownHostException e) {
  275. return "localhost"; // NOI18N
  276. }
  277. }
  278. public void addGrantAccessListener(GrantAccessListener l) {
  279. listenerList.add(GrantAccessListener.class, l);
  280. }
  281. public void removeGrantAccessListener(GrantAccessListener l) {
  282. listenerList.remove(GrantAccessListener.class, l);
  283. }
  284. /** Returns true if oneof the listeners allowed access */
  285. protected boolean fireGrantAccessEvent(InetAddress clientAddress, String resource) {
  286. Object[] listeners = listenerList.getListenerList();
  287. GrantAccessEvent grantAccessEvent = null;
  288. for (int i = listeners.length-2; i>=0; i-=2) {
  289. if (listeners[i]==GrantAccessListener.class) {
  290. if (grantAccessEvent == null)
  291. grantAccessEvent = new GrantAccessEvent(this, clientAddress, resource);
  292. ((GrantAccessListener)listeners[i+1]).grantAccess(grantAccessEvent);
  293. }
  294. }
  295. return (grantAccessEvent == null) ? false : grantAccessEvent.isGranted();
  296. }
  297. /** Requests access for address addr. If necessary asks the user. Returns true it the access
  298. * has been granted. */
  299. boolean allowAccess(InetAddress addr, String requestPath) {
  300. if (accessAllowedNow(addr, requestPath))
  301. return true;
  302. Thread askThread = null;
  303. synchronized (whoAsking) {
  304. // one more test in the synchronized block
  305. if (accessAllowedNow(addr, requestPath))
  306. return true;
  307. askThread = (Thread)whoAsking.get(addr);
  308. if (askThread == null) {
  309. askThread = Thread.currentThread();
  310. whoAsking.put(addr, askThread);
  311. }
  312. }
  313. // now ask the user
  314. synchronized (HttpServerSettings.class) {
  315. if (askThread != Thread.currentThread()) {
  316. return accessAllowedNow(addr, requestPath);
  317. }
  318. try {
  319. if (!isShowGrantAccessDialog ())
  320. return false;
  321. String msg = NbBundle.getMessage (HttpServerSettings.class, "MSG_AddAddress", addr.getHostAddress ());
  322. final GrantAccessPanel panel = new GrantAccessPanel (msg);
  323. DialogDescriptor descriptor = new DialogDescriptor (
  324. panel,
  325. NbBundle.getMessage (HttpServerSettings.class, "CTL_GrantAccessTitle"),
  326. true,
  327. NotifyDescriptor.YES_NO_OPTION,
  328. NotifyDescriptor.NO_OPTION,
  329. null
  330. );
  331. descriptor.setMessageType (NotifyDescriptor.QUESTION_MESSAGE);
  332. // descriptor.setOptionsAlign (DialogDescriptor.BOTTOM_ALIGN);
  333. final Dialog d = DialogDisplayer.getDefault ().createDialog (descriptor);
  334. d.setSize (580, 180);
  335. d.setVisible(true);
  336. setShowGrantAccessDialog (panel.getShowDialog ());
  337. if (NotifyDescriptor.YES_OPTION.equals(descriptor.getValue ())) {
  338. appendAddressToGranted(addr.getHostAddress());
  339. return true;
  340. }
  341. else
  342. return false;
  343. }
  344. finally {
  345. whoAsking.remove(addr);
  346. }
  347. } // end synchronized
  348. }
  349. /** Checks whether access to the server is now allowed. */
  350. private boolean accessAllowedNow(InetAddress addr, String resource) {
  351. if (hostProperty.getHost().equals(HttpServerSettings.ANYHOST))
  352. return true;
  353. Set hs = getGrantedAddressesSet();
  354. if (hs.contains(addr.getHostAddress()))
  355. return true;
  356. if (fireGrantAccessEvent(addr, resource))
  357. return true;
  358. return false;
  359. }
  360. /** Appends the address to the list of addresses which have been granted access. */
  361. private void appendAddressToGranted(String addr) {
  362. synchronized (httpLock ()) {
  363. String granted = hostProperty.getGrantedAddresses().trim();
  364. if ((granted.length() > 0) &&
  365. (granted.charAt(granted.length() - 1) != ';') &&
  366. (granted.charAt(granted.length() - 1) != ','))
  367. granted += ',';
  368. granted += addr;
  369. hostProperty.setGrantedAddresses(granted);
  370. }
  371. }
  372. /** Returns a list of addresses which have been granted access to the web server,
  373. * including the localhost. Addresses are represented as strings. */
  374. Set<String> getGrantedAddressesSet() {
  375. HashSet<String> addr = new HashSet<String>();
  376. try {
  377. addr.add(InetAddress.getByName("localhost").getHostAddress()); // NOI18N
  378. addr.add(InetAddress.getLocalHost().getHostAddress());
  379. }
  380. catch (UnknownHostException e) {}
  381. StringTokenizer st = new StringTokenizer(hostProperty.getGrantedAddresses(), ",;"); // NOI18N
  382. while (st.hasMoreTokens()) {
  383. String ipa = st.nextToken();
  384. ipa = ipa.trim();
  385. try {
  386. addr.add(InetAddress.getByName(ipa).getHostAddress());
  387. }
  388. catch (UnknownHostException e) {}
  389. }
  390. return addr;
  391. }
  392. Properties getMappedServlets() {
  393. return mappedServlets;
  394. }
  395. /** Converts string into string that is usable in URL.
  396. * This mangling changes some characters
  397. */
  398. static String mangle (String name) {
  399. StringBuffer sb = new StringBuffer ();
  400. for (int i = 0; i < name.length (); i++) {
  401. if (Character.isLetterOrDigit (name.charAt (i)) ||
  402. name.charAt (i) == '.') {
  403. sb.append (name.charAt (i));
  404. }
  405. else {
  406. String code = Integer.toHexString ((int)name.charAt (i)).toUpperCase ();
  407. if (code.length ()<2)
  408. code = (code.length () == 0)? "00": "0"+code; // NOI18N
  409. sb.append ("%"). // NOI18N
  410. append ((code.length () == 2)? code: code.substring (code.length ()-2));
  411. }
  412. }
  413. return sb.toString ();
  414. }
  415. /** Unconverts string from URL into old string.
  416. * This mangling decodes '%xy'
  417. */
  418. static String demangle (String name) {
  419. StringBuffer sb = new StringBuffer ();
  420. try {
  421. for (int i = 0; i < name.length (); i++) {
  422. if (name.charAt (i) != '%') {
  423. sb.append (name.charAt (i));
  424. }
  425. else {
  426. sb.append ((char)Integer.parseInt (name.substring (i+1, i+3), 16));
  427. i += 2;
  428. }
  429. }
  430. }
  431. catch (NumberFormatException ex) {
  432. ex.printStackTrace ();
  433. return ""; // NOI18N
  434. }
  435. return sb.toString ();
  436. }
  437. /** Getter for property hostProperty.
  438. * @return Value of property hostProperty.
  439. */
  440. public HttpServerSettings.HostProperty getHostProperty () {
  441. if (hostProperty == null) {
  442. hostProperty = new HostProperty(getPreferences().get("grantedAddresses",""),
  443. getPreferences().get("host",LOCALHOST));
  444. }
  445. return hostProperty;
  446. }
  447. /** Setter for property hostProperty.
  448. * @param hostProperty New value of property hostProperty.
  449. */
  450. public void setHostProperty (HttpServerSettings.HostProperty hostProperty) {
  451. if (ANYHOST.equals(hostProperty.getHost ()) || LOCALHOST.equals(hostProperty.getHost ())) {
  452. this.hostProperty.setHost(hostProperty.getHost());
  453. this.hostProperty.setGrantedAddresses(hostProperty.getGrantedAddresses());
  454. getPreferences().put("host", hostProperty.getHost());//NOI18N
  455. getPreferences().put("grantedAddresses", hostProperty.getGrantedAddresses());//NOI18N
  456. }
  457. }
  458. public boolean isShowGrantAccessDialog () {
  459. return getPreferences().getBoolean(PROP_SHOW_GRANT_ACCESS, true);
  460. }
  461. public void setShowGrantAccessDialog (boolean show) {
  462. getPreferences().putBoolean(PROP_SHOW_GRANT_ACCESS,show);
  463. }
  464. /** Property value that describes set of host with granted access
  465. */
  466. public static class HostProperty implements java.io.Serializable {
  467. private String grantedAddresses;
  468. private String host;
  469. private static final long serialVersionUID = 1927848926692414249L;
  470. HostProperty (String grantedAddresses, String host) {
  471. this.grantedAddresses = grantedAddresses;
  472. this.host = host;
  473. }
  474. /** Getter for property host.
  475. * @return Value of property host.
  476. */
  477. public String getHost () {
  478. return host;
  479. }
  480. /** Setter for property host.
  481. * @param host New value of property host.
  482. */
  483. public void setHost (String host) {
  484. this.host = host;
  485. }
  486. /** Getter for property grantedAddresses.
  487. * @return Value of property grantedAddresses.
  488. */
  489. public String getGrantedAddresses () {
  490. return grantedAddresses;
  491. }
  492. /** Setter for property grantedAddresses.
  493. * @param grantedAddresses New value of property grantedAddresses.
  494. */
  495. public void setGrantedAddresses (String grantedAddresses) {
  496. this.grantedAddresses = grantedAddresses;
  497. }
  498. }
  499. }