KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > gjt > sp > jedit > EditServer


1 /*
2  * EditServer.java - jEdit server
3  * :tabSize=8:indentSize=8:noTabs=false:
4  * :folding=explicit:collapseFolds=1:
5  *
6  * Copyright (C) 1999, 2003 Slava Pestov
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21  */

22
23 package org.gjt.sp.jedit;
24
25 //{{{ Imports
26
import bsh.NameSpace;
27 import javax.swing.SwingUtilities JavaDoc;
28 import java.io.*;
29 import java.net.*;
30 import java.util.Random JavaDoc;
31 import org.gjt.sp.jedit.io.FileVFS;
32 import org.gjt.sp.util.Log;
33 //}}}
34

35 /**
36  * Inter-process communication.<p>
37  *
38  * The edit server protocol is very simple. <code>$HOME/.jedit/server</code>
39  * is an ASCII file containing two lines, the first being the port number,
40  * the second being the authorization key.<p>
41  *
42  * You connect to that port on the local machine, sending the authorization
43  * key as four bytes in network byte order, followed by the length of the
44  * BeanShell script as two bytes in network byte order, followed by the
45  * script in UTF8 encoding. After the socked is closed, the BeanShell script
46  * will be executed by jEdit.<p>
47  *
48  * The snippet is executed in the AWT thread. None of the usual BeanShell
49  * variables (view, buffer, textArea, editPane) are set so the script has to
50  * figure things out by itself.<p>
51  *
52  * In most cases, the script will call the static
53  * {@link #handleClient(boolean,String,String[])} method, but of course more
54  * complicated stuff can be done too.
55  *
56  * @author Slava Pestov
57  * @version $Id: EditServer.java 5102 2004-08-08 03:41:35Z spestov $
58  */

59 public class EditServer extends Thread JavaDoc
60 {
61     //{{{ EditServer constructor
62
EditServer(String JavaDoc portFile)
63     {
64         super("jEdit server daemon [" + portFile + "]");
65         setDaemon(true);
66         this.portFile = portFile;
67
68         try
69         {
70             // On Unix, set permissions of port file to rw-------,
71
// so that on broken Unices which give everyone read
72
// access to user home dirs, people can't see your
73
// port file (and hence send arbitriary BeanShell code
74
// your way. Nasty.)
75
if(OperatingSystem.isUnix())
76             {
77                 new File(portFile).createNewFile();
78                 FileVFS.setPermissions(portFile,0600);
79             }
80
81             // Bind to any port on localhost; accept 2 simultaneous
82
// connection attempts before rejecting connections
83
socket = new ServerSocket(0, 2,
84                 InetAddress.getByName("127.0.0.1"));
85             authKey = Math.abs(new Random JavaDoc().nextInt());
86             int port = socket.getLocalPort();
87
88             FileWriter out = new FileWriter(portFile);
89
90             try
91             {
92                 out.write("b\n");
93                 out.write(String.valueOf(port));
94                 out.write("\n");
95                 out.write(String.valueOf(authKey));
96                 out.write("\n");
97             }
98             finally
99             {
100                 out.close();
101             }
102
103             ok = true;
104
105             Log.log(Log.DEBUG,this,"jEdit server started on port "
106                 + socket.getLocalPort());
107             Log.log(Log.DEBUG,this,"Authorization key is "
108                 + authKey);
109         }
110         catch(IOException io)
111         {
112             /* on some Windows versions, connections to localhost
113              * fail if the network is not running. To avoid
114              * confusing newbies with weird error messages, log
115              * errors that occur while starting the server
116              * as NOTICE, not ERROR */

117             Log.log(Log.NOTICE,this,io);
118         }
119     } //}}}
120

121     //{{{ run() method
122
public void run()
123     {
124         for(;;)
125         {
126             if(abort)
127                 return;
128
129             Socket client = null;
130             try
131             {
132                 client = socket.accept();
133
134                 // Stop script kiddies from opening the edit
135
// server port and just leaving it open, as a
136
// DoS
137
client.setSoTimeout(1000);
138
139                 Log.log(Log.MESSAGE,this,client + ": connected");
140
141                 DataInputStream in = new DataInputStream(
142                     client.getInputStream());
143
144                 if(!handleClient(client,in))
145                     abort = true;
146             }
147             catch(Exception JavaDoc e)
148             {
149                 if(!abort)
150                     Log.log(Log.ERROR,this,e);
151                 abort = true;
152             }
153             finally
154             {
155                 /* if(client != null)
156                 {
157                     try
158                     {
159                         client.close();
160                     }
161                     catch(Exception e)
162                     {
163                         Log.log(Log.ERROR,this,e);
164                     }
165
166                     client = null;
167                 } */

168             }
169         }
170     } //}}}
171

172     //{{{ handleClient() method
173
/**
174      * @param restore Ignored unless no views are open
175      * @param parent The client's parent directory
176      * @param args A list of files. Null entries are ignored, for convinience
177      * @since jEdit 3.2pre7
178      */

179     public static void handleClient(boolean restore, String JavaDoc parent,
180         String JavaDoc[] args)
181     {
182         handleClient(restore,false,false,parent,args);
183     } //}}}
184

185     //{{{ handleClient() method
186
/**
187      * @param restore Ignored unless no views are open
188      * @param newView Open a new view?
189      * @param newPlainView Open a new plain view?
190      * @param parent The client's parent directory
191      * @param args A list of files. Null entries are ignored, for convinience
192      * @since jEdit 4.2pre1
193      */

194     public static Buffer handleClient(boolean restore,
195         boolean newView, boolean newPlainView, String JavaDoc parent,
196         String JavaDoc[] args)
197     {
198         // we have to deal with a huge range of possible border cases here.
199
if(jEdit.getFirstView() == null)
200         {
201             // coming out of background mode.
202
// no views open.
203
// no buffers open if args empty.
204

205             Buffer buffer = jEdit.openFiles(null,parent,args);
206
207             if(jEdit.getBufferCount() == 0)
208                 jEdit.newFile(null);
209
210             boolean restoreFiles = restore
211                 && jEdit.getBooleanProperty("restore")
212                 && (buffer == null
213                 || jEdit.getBooleanProperty("restore.cli"));
214
215             View view = PerspectiveManager.loadPerspective(
216                 restoreFiles);
217
218             if(view == null)
219             {
220                 if(buffer == null)
221                     buffer = jEdit.getFirstBuffer();
222                 view = jEdit.newView(null,buffer);
223             }
224             else if(buffer != null)
225                 view.setBuffer(buffer);
226
227             return buffer;
228         }
229         else if(newPlainView)
230         {
231             // no background mode, and opening a new view
232
Buffer buffer = jEdit.openFiles(null,parent,args);
233             if(buffer == null)
234                 buffer = jEdit.getFirstBuffer();
235             jEdit.newView(null,buffer,true);
236             return buffer;
237         }
238         else if(newView)
239         {
240             // no background mode, and opening a new view
241
Buffer buffer = jEdit.openFiles(null,parent,args);
242             if(buffer == null)
243                 buffer = jEdit.getFirstBuffer();
244             jEdit.newView(jEdit.getActiveView(),buffer,false);
245             return buffer;
246         }
247         else
248         {
249             // no background mode, and reusing existing view
250
View view = jEdit.getActiveView();
251
252             Buffer buffer = jEdit.openFiles(view,parent,args);
253
254             // Hack done to fix bringing the window to the front.
255
// At least on windows, Frame.toFront() doesn't cut it.
256
// Remove the isWindows check if it's broken under other
257
// OSes too.
258
if (jEdit.getBooleanProperty("server.brokenToFront"))
259                 view.setState(java.awt.Frame.ICONIFIED);
260
261             // un-iconify using JDK 1.3 API
262
view.setState(java.awt.Frame.NORMAL);
263             view.requestFocus();
264             view.toFront();
265
266             return buffer;
267         }
268     } //}}}
269

270     //{{{ isOK() method
271
boolean isOK()
272     {
273         return ok;
274     } //}}}
275

276     //{{{ getPort method
277
public int getPort()
278     {
279         return socket.getLocalPort();
280     } //}}}
281

282     //{{{ stopServer() method
283
void stopServer()
284     {
285         abort = true;
286         try
287         {
288             socket.close();
289         }
290         catch(IOException io)
291         {
292         }
293
294         new File(portFile).delete();
295     } //}}}
296

297     //{{{ Private members
298

299     //{{{ Instance variables
300
private String JavaDoc portFile;
301     private ServerSocket socket;
302     private int authKey;
303     private boolean ok;
304     private boolean abort;
305     //}}}
306

307     //{{{ handleClient() method
308
private boolean handleClient(final Socket client, DataInputStream in)
309         throws Exception JavaDoc
310     {
311         int key = in.readInt();
312         if(key != authKey)
313         {
314             Log.log(Log.ERROR,this,client + ": wrong"
315                 + " authorization key (got " + key
316                 + ", expected " + authKey + ")");
317             in.close();
318             client.close();
319
320             return false;
321         }
322         else
323         {
324             // Reset the timeout
325
client.setSoTimeout(0);
326
327             Log.log(Log.DEBUG,this,client + ": authenticated"
328                 + " successfully");
329
330             final String JavaDoc script = in.readUTF();
331             Log.log(Log.DEBUG,this,script);
332
333             SwingUtilities.invokeLater(new Runnable JavaDoc()
334             {
335                 public void run()
336                 {
337                     try
338                     {
339                         NameSpace ns = new NameSpace(
340                             BeanShell.getNameSpace(),
341                             "EditServer namespace");
342                         ns.setVariable("socket",client);
343                         BeanShell.eval(null,ns,script);
344                     }
345                     catch(bsh.UtilEvalError e)
346                     {
347                         Log.log(Log.ERROR,this,e);
348                     }
349                     finally
350                     {
351                         try
352                         {
353                             BeanShell.getNameSpace().setVariable("socket",null);
354                         }
355                         catch(bsh.UtilEvalError e)
356                         {
357                             Log.log(Log.ERROR,this,e);
358                         }
359                     }
360                 }
361             });
362
363             return true;
364         }
365     } //}}}
366

367     //}}}
368
}
369
Popular Tags