KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > ruby > railsprojects > server > WebrickServer


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19 package org.netbeans.modules.ruby.railsprojects.server;
20
21 import java.awt.Dialog JavaDoc;
22 import java.awt.event.ActionEvent JavaDoc;
23 import java.io.BufferedReader JavaDoc;
24 import java.io.File JavaDoc;
25 import java.io.IOException JavaDoc;
26 import java.io.InputStreamReader JavaDoc;
27 import java.io.PrintWriter JavaDoc;
28 import java.net.InetSocketAddress JavaDoc;
29 import java.net.MalformedURLException JavaDoc;
30 import java.net.Socket JavaDoc;
31 import java.net.URL JavaDoc;
32 import java.util.ArrayList JavaDoc;
33 import java.util.List JavaDoc;
34 import javax.swing.AbstractAction JavaDoc;
35 import org.netbeans.modules.ruby.rubyproject.api.RubyExecution;
36 import org.netbeans.modules.ruby.rubyproject.execution.DirectoryFileLocator;
37 import org.netbeans.modules.ruby.rubyproject.execution.ExecutionService;
38 import org.netbeans.modules.ruby.rubyproject.api.RubyInstallation;
39 import org.netbeans.api.progress.ProgressHandle;
40 import org.netbeans.api.progress.ProgressHandleFactory;
41 import org.netbeans.modules.ruby.rubyproject.execution.ExecutionDescriptor;
42 import org.netbeans.modules.ruby.rubyproject.execution.OutputRecognizer;
43 import org.netbeans.modules.ruby.rubyproject.execution.OutputRecognizer.RecognizedOutput;
44 import org.openide.DialogDescriptor;
45 import org.openide.DialogDisplayer;
46 import org.openide.awt.HtmlBrowser;
47 import org.openide.awt.StatusDisplayer;
48 import org.openide.filesystems.FileUtil;
49 import org.openide.util.Cancellable;
50 import org.openide.util.NbBundle;
51 import org.openide.util.RequestProcessor;
52 import org.netbeans.api.project.Project;
53 import org.netbeans.api.project.ProjectInformation;
54 import org.openide.ErrorManager;
55
56
57 /**
58  * Support for the builtin Ruby On Rails web server: WEBrick
59  *
60  * This is really primitive at this point; I should talk to the people who
61  * write Java web server plugins and take some pointers. Perhaps it can
62  * even implement some of their APIs such that logging, runtime nodes etc.
63  * all begin to work.
64  * @author Tor Norbye, Pavel Buzek
65  */

66 public class WebrickServer {
67     private ServerStatus status = ServerStatus.NOT_STARTED;
68     private boolean cancelled;
69     private boolean portConflict;
70     private int port = -1;
71     private Project project;
72     File JavaDoc dir;
73
74     public WebrickServer(Project project) {
75         this.project = project;
76         dir = FileUtil.toFile(project.getProjectDirectory());
77     }
78
79     public void ensureRunning() {
80         synchronized (WebrickServer.this) {
81             if (status == ServerStatus.STARTING) {
82                 return;
83             } else if (status == ServerStatus.RUNNING) {
84                 if (isPortInUse(port)) {
85                     // Simply assume it is still the same server running
86
return;
87                 }
88             }
89         }
90
91         // Server was not started or was killed externally
92
Runnable JavaDoc finishedAction =
93             new Runnable JavaDoc() {
94                 public void run() {
95                     synchronized (WebrickServer.this) {
96                         status = ServerStatus.NOT_STARTED;
97                         if (portConflict) {
98                             // Port conflict - notify user.
99
updatePort();
100                         }
101                     }
102                 }
103             };
104
105         // Start the server
106
synchronized (WebrickServer.this) {
107             status = ServerStatus.STARTING;
108         }
109
110         portConflict = false;
111         if (port == -1) {
112             port = RubyInstallation.getInstance().getRailsPort();
113         }
114         while(isPortInUse(port)) {
115             port++;
116         }
117         String JavaDoc name = project.getLookup().lookup(ProjectInformation.class).getDisplayName();
118         new ExecutionService(new ExecutionDescriptor(NbBundle.getMessage(WebrickServer.class, "WEBrickTab", name, Integer.toString(port)), dir,
119             "script" + File.separator + "server", "--port", Integer.toString(port)).postBuild(finishedAction) // NOI18N
120
.addOutputRecognizer(RubyExecution.RUBY_COMPILER)
121                .addOutputRecognizer(new WebrickMessageListener())
122                .fileLocator(new DirectoryFileLocator(FileUtil.toFileObject(dir)))
123                .showProgress(false))
124                .run();
125     }
126     
127     private void updatePort() {
128             final PortConflictPanel panel = new PortConflictPanel(RubyInstallation.getInstance().getRailsPort());
129             //final JButton okButton = new JButton (NbBundle.getMessage (WebrickServer.class, "Ok"));
130
Object JavaDoc[] options = new Object JavaDoc[] {
131                 //okButton,
132
DialogDescriptor.OK_OPTION,
133                 DialogDescriptor.CANCEL_OPTION
134             };
135 // panel.addChangeListener (new ChangeListener () {
136
// public void stateChanged(ChangeEvent e) {
137
// if (e.getSource () instanceof MouseEvent && MouseUtils.isDoubleClick (((MouseEvent)e.getSource ()))) {
138
// // click button and finish the dialog with selected class
139
// okButton.doClick ();
140
// } else {
141
// okButton.setEnabled (panel.getSelectedMainClass () != null);
142
// }
143
// }
144
// });
145
// okButton.setEnabled (false);
146
DialogDescriptor desc = new DialogDescriptor (
147                 panel,
148                 NbBundle.getMessage (WebrickServer.class, "LBL_PortTitle" ),
149                 true,
150                 options,
151                 options[0],
152                 DialogDescriptor.BOTTOM_ALIGN,
153                 null,
154                 null);
155             //desc.setMessageType (DialogDescriptor.INFORMATION_MESSAGE);
156
Dialog JavaDoc dlg = DialogDisplayer.getDefault ().createDialog (desc);
157             dlg.setVisible (true);
158             if (desc.getValue() == options[0]) {
159                 RubyInstallation.getInstance().setRailsPort(panel.getPort());
160                 // Restart server
161
ensureRunning();
162             }
163             dlg.dispose();
164     }
165
166     /** Starts the server if not running and shows url.
167      * @param relativeUrl the resulting url will be for example: http://localhost:3001/{relativeUrl}
168      */

169     public synchronized void showUrl(final String JavaDoc relativeUrl) {
170         synchronized (WebrickServer.this) {
171             if (status == ServerStatus.RUNNING && isPortInUse(port)) {
172                 try {
173                     URL JavaDoc url = new URL JavaDoc("http://localhost:" + port + "/" + relativeUrl); // NOI18N
174
HtmlBrowser.URLDisplayer.getDefault().showURL(url);
175                 } catch (MalformedURLException JavaDoc ex) {
176                     ErrorManager.getDefault().notify(ex);
177                 }
178                 return;
179             }
180
181             ensureRunning();
182         }
183
184         cancelled = false;
185
186         String JavaDoc displayName = NbBundle.getMessage(WebrickServer.class, "ServerStartup");
187         final ProgressHandle handle =
188             ProgressHandleFactory.createHandle(displayName,
189                 new Cancellable() {
190                     public boolean cancel() {
191                         synchronized (WebrickServer.this) {
192                             cancelled = true;
193                         }
194
195                         return true;
196                     }
197                 },
198                 new AbstractAction JavaDoc() {
199                     public void actionPerformed(ActionEvent JavaDoc e) {
200                         // XXX ?
201
}
202                 });
203
204         handle.start();
205         handle.switchToIndeterminate();
206
207         RequestProcessor.getDefault().post(new Runnable JavaDoc() {
208                 public void run() {
209                     try {
210                         // Try connecting repeatedly, up to 20 seconds, then bail
211
for (int i = 0; i < 20; i++) {
212                             try {
213                                 Thread.currentThread().sleep(1000);
214                             } catch (InterruptedException JavaDoc ie) {
215                                 ; // Don't worry about it
216
}
217
218                             synchronized (WebrickServer.this) {
219                                 if (status == ServerStatus.RUNNING) {
220                                     try {
221                                         URL JavaDoc url = new URL JavaDoc("http://localhost:" + port + "/" + relativeUrl); // NOI18N
222
HtmlBrowser.URLDisplayer.getDefault().showURL(url);
223                                     } catch (MalformedURLException JavaDoc ex) {
224                                         ErrorManager.getDefault().notify(ex);
225                                     }
226
227                                     return;
228                                 }
229
230                                 if (status == ServerStatus.NOT_STARTED) {
231                                     // Server startup somehow failed...
232
break;
233                                 }
234                             }
235
236                             /* My attempts to do URLConnections didn't pan out.... so just do a simple
237                              * listener based scheme instead based on parsing build output with the
238                              * OutputRecognizer
239                                 URLConnection connection = url.openConnection();
240                                 connection.setConnectTimeout(1000); // 1 second
241
242                                 if (connection instanceof HttpURLConnection) {
243                                     HttpURLConnection c = (HttpURLConnection)connection;
244                                     c.setRequestMethod("POST");
245                                     c.setFollowRedirects(true);
246
247                                     // Try connecting repeatedly, up to 20 seconds, then bail
248                                     synchronized (WebrickServer.this) {
249                                         if (status == ServerStatus.NOT_STARTED) {
250                                             // Server startup somehow failed...
251                                             break;
252                                         }
253                                     }
254
255                                     try {
256                                         c.connect();
257                                         StatusDisplayer.getDefault()
258                                                        .setStatusText("Connect attempt #" + i +
259                                             " status was " + c.getResponseCode() + " : " +
260                                             c.getResponseMessage() + " : " +
261                                             c.getHeaderFields().toString());
262
263                                         if (c.getResponseCode() == HttpURLConnection.HTTP_OK) {
264                                             synchronized (WebrickServer.this) {
265                                                 status = ServerStatus.RUNNING;
266                                             }
267
268                                             HtmlBrowser.URLDisplayer.getDefault().showURL(url);
269
270                                             return;
271                                         }
272
273                                         // Disconnect?
274                                         //c.disconnect();
275                                         try {
276                                             Thread.currentThread().sleep(1000);
277                                         } catch (InterruptedException ie) {
278                                             ; // Don't worry about it
279                                         }
280                                     } catch (ConnectException ce) {
281                                         // wait 1 second and try again
282                                         try {
283                                             Thread.currentThread().sleep(1000);
284                                         } catch (InterruptedException ie) {
285                                             ; // Don't worry about it
286                                         }
287                                     }
288                                 }
289                             */

290                         }
291
292                         StatusDisplayer.getDefault()
293                                        .setStatusText(NbBundle.getMessage(WebrickServer.class,
294                                 "NoServerFound", "http://localhost:" + port + "/" + relativeUrl));
295
296                         //} catch (IOException ioe) {
297
// ErrorManager.getDefault().notify(ioe);
298
} finally {
299                         handle.finish();
300                     }
301                 }
302             });
303     }
304
305     /** Return true if there is an HTTP response from the port on localhost.
306      * Based on tomcatint\tomcat5\src\org.netbeans.modules.tomcat5.util.Utils.java.
307      */

308     public static boolean isPortInUse(int port) {
309         int timeout = 3000;
310         Socket JavaDoc socket = new Socket JavaDoc();
311         try {
312             try {
313                 socket.connect(new InetSocketAddress JavaDoc("localhost", port), timeout); // NOI18N
314
socket.setSoTimeout(timeout);
315                 PrintWriter JavaDoc out = new PrintWriter JavaDoc(socket.getOutputStream(), true);
316                 try {
317                     BufferedReader JavaDoc in = new BufferedReader JavaDoc(new InputStreamReader JavaDoc(socket.getInputStream()));
318                     try {
319                         // request
320
out.println("GET /\n"); // NOI18N
321

322                         // response
323
String JavaDoc text = in.readLine();
324                         if (text == null || !text.startsWith("<!DOCTYPE")) { // NOI18N
325
return false; // not an http response
326
}
327                         return true;
328                     } finally {
329                         in.close();
330                     }
331                 } finally {
332                     out.close();
333                 }
334             } finally {
335                 socket.close();
336             }
337         } catch (IOException JavaDoc ioe) {
338             return false;
339         }
340     }
341     
342     private class WebrickMessageListener extends OutputRecognizer {
343         public RecognizedOutput processLine(String JavaDoc line) {
344             // This is ugly, but my attempts to use URLConnection on the URL repeatedly
345
// and check for connection.getResponseCode()==HttpURLConnection.HTTP_OK didn't
346
// work - try that again later
347
if (line.contains("Rails application started on")) { // NOI18N
348

349                 synchronized (WebrickServer.this) {
350                     status = ServerStatus.RUNNING;
351                 }
352             } else if (line.contains("in `new': Address in use (Errno::EADDRINUSE)")) {
353                 portConflict = true;
354             }
355
356             return null;
357         }
358     }
359     enum ServerStatus {NOT_STARTED, STARTING, RUNNING;
360     }
361 }
362
Popular Tags