EMMA Coverage Report (generated Sun Mar 01 22:06:14 CET 2015)
[all classes][org.h2.server.web]

COVERAGE SUMMARY FOR SOURCE FILE [WebServer.java]

nameclass, %method, %block, %line, %
WebServer.java50%  (1/2)79%  (42/53)79%  (1364/1719)69%  (228/330)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class WebServer$TranslateThread0%   (0/1)0%   (0/4)0%   (0/75)0%   (0/25)
WebServer$TranslateThread (WebServer, Map): void 0%   (0/1)0%   (0/15)0%   (0/4)
getFileName (): String 0%   (0/1)0%   (0/4)0%   (0/1)
run (): void 0%   (0/1)0%   (0/48)0%   (0/15)
stopNow (): void 0%   (0/1)0%   (0/8)0%   (0/5)
     
class WebServer100% (1/1)86%  (42/49)83%  (1364/1644)75%  (228/305)
saveCommandHistoryList (ArrayList): void 0%   (0/1)0%   (0/40)0%   (0/9)
setAllowOthers (boolean): void 0%   (0/1)0%   (0/4)0%   (0/2)
setCommandHistoryAllowed (boolean): void 0%   (0/1)0%   (0/13)0%   (0/5)
setPort (int): void 0%   (0/1)0%   (0/4)0%   (0/2)
setSSL (boolean): void 0%   (0/1)0%   (0/4)0%   (0/2)
startTranslate (Map): String 0%   (0/1)0%   (0/24)0%   (0/6)
traceError (Throwable): void 0%   (0/1)0%   (0/6)0%   (0/3)
getCommandHistoryList (): ArrayList 100% (1/1)9%   (7/77)20%  (3/15)
trace (String): void 100% (1/1)57%  (4/7)67%  (2/3)
isRunning (boolean): boolean 100% (1/1)65%  (15/23)56%  (5/9)
isCommandHistoryAllowed (): boolean 100% (1/1)71%  (5/7)71%  (0.7/1)
loadProperties (): Properties 100% (1/1)76%  (22/29)50%  (3/6)
saveProperties (Properties): void 100% (1/1)77%  (98/127)73%  (16/22)
getSession (String): WebSession 100% (1/1)78%  (52/67)85%  (11/13)
stop (): void 100% (1/1)83%  (54/65)73%  (16/22)
readTranslations (WebSession, String): void 100% (1/1)89%  (76/85)80%  (12/15)
getConnection (String, String, String, String): Connection 100% (1/1)90%  (44/49)91%  (10/11)
getFile (String): byte [] 100% (1/1)90%  (73/81)79%  (11/14)
init (String []): void 100% (1/1)91%  (143/158)85%  (26.3/31)
isSimpleName (String): boolean 100% (1/1)94%  (31/33)75%  (3/4)
updateURL (): void 100% (1/1)96%  (24/25)75%  (3/4)
<static initializer> 100% (1/1)100% (326/326)100% (3/3)
WebServer (): void 100% (1/1)100% (24/24)100% (8/8)
addSession (Connection): String 100% (1/1)100% (32/32)100% (6/6)
createNewSession (String): WebSession 100% (1/1)100% (49/49)100% (12/12)
generateSessionId (): String 100% (1/1)100% (6/6)100% (2/2)
getAllowChunked (): boolean 100% (1/1)100% (3/3)100% (1/1)
getAllowOthers (): boolean 100% (1/1)100% (3/3)100% (1/1)
getName (): String 100% (1/1)100% (2/2)100% (1/1)
getPort (): int 100% (1/1)100% (3/3)100% (1/1)
getSSL (): boolean 100% (1/1)100% (3/3)100% (1/1)
getSessions (): ArrayList 100% (1/1)100% (22/22)100% (5/5)
getSetting (String): ConnectionInfo 100% (1/1)100% (6/6)100% (1/1)
getSettingNames (): String [] 100% (1/1)100% (25/25)100% (5/5)
getSettings (): ArrayList 100% (1/1)100% (76/76)100% (19/19)
getStartDateTime (): String 100% (1/1)100% (26/26)100% (5/5)
getType (): String 100% (1/1)100% (2/2)100% (1/1)
getURL (): String 100% (1/1)100% (5/5)100% (2/2)
isDaemon (): boolean 100% (1/1)100% (3/3)100% (1/1)
isStopped (): boolean 100% (1/1)100% (7/7)100% (1/1)
listen (): void 100% (1/1)100% (31/31)100% (11/11)
remove (WebThread): void 100% (1/1)100% (6/6)100% (2/2)
removeSetting (String): void 100% (1/1)100% (6/6)100% (2/2)
setAllowChunked (boolean): void 100% (1/1)100% (4/4)100% (2/2)
setShutdownHandler (ShutdownHandler): void 100% (1/1)100% (4/4)100% (2/2)
shutdown (): void 100% (1/1)100% (7/7)100% (3/3)
start (): void 100% (1/1)100% (15/15)100% (4/4)
supportsLanguage (String): boolean 100% (1/1)100% (5/5)100% (1/1)
updateSetting (ConnectionInfo): void 100% (1/1)100% (15/15)100% (3/3)

1/*
2 * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0,
3 * and the EPL 1.0 (http://h2database.com/html/license.html).
4 * Initial Developer: H2 Group
5 */
6package org.h2.server.web;
7 
8import java.io.File;
9import java.io.FileInputStream;
10import java.io.IOException;
11import java.io.InputStream;
12import java.io.OutputStream;
13import java.net.ServerSocket;
14import java.net.Socket;
15import java.sql.Connection;
16import java.sql.SQLException;
17import java.text.SimpleDateFormat;
18import java.util.ArrayList;
19import java.util.Collections;
20import java.util.HashMap;
21import java.util.HashSet;
22import java.util.Locale;
23import java.util.Map;
24import java.util.Map.Entry;
25import java.util.Properties;
26import java.util.Set;
27import java.util.TimeZone;
28 
29import org.h2.engine.Constants;
30import org.h2.engine.SysProperties;
31import org.h2.message.DbException;
32import org.h2.server.Service;
33import org.h2.server.ShutdownHandler;
34import org.h2.store.fs.FileUtils;
35import org.h2.util.IOUtils;
36import org.h2.util.JdbcUtils;
37import org.h2.util.MathUtils;
38import org.h2.util.NetUtils;
39import org.h2.util.New;
40import org.h2.util.SortedProperties;
41import org.h2.util.StringUtils;
42import org.h2.util.Tool;
43import org.h2.util.Utils;
44 
45/**
46 * The web server is a simple standalone HTTP server that implements the H2
47 * Console application. It is not optimized for performance.
48 */
49public class WebServer implements Service {
50 
51    static final String TRANSFER = "transfer";
52 
53    static final String[][] LANGUAGES = {
54        { "cs", "\u010ce\u0161tina" },
55        { "de", "Deutsch" },
56        { "en", "English" },
57        { "es", "Espa\u00f1ol" },
58        { "fr", "Fran\u00e7ais" },
59        { "hu", "Magyar"},
60        { "ko", "\ud55c\uad6d\uc5b4"},
61        { "in", "Indonesia"},
62        { "it", "Italiano"},
63        { "ja", "\u65e5\u672c\u8a9e"},
64        { "nl", "Nederlands"},
65        { "pl", "Polski"},
66        { "pt_BR", "Portugu\u00eas (Brasil)"},
67        { "pt_PT", "Portugu\u00eas (Europeu)"},
68        { "ru", "\u0440\u0443\u0441\u0441\u043a\u0438\u0439"},
69        { "sk", "Slovensky"},
70        { "tr", "T\u00fcrk\u00e7e"},
71        { "uk", "\u0423\u043A\u0440\u0430\u0457\u043D\u0441\u044C\u043A\u0430"},
72        { "zh_CN", "\u4e2d\u6587 (\u7b80\u4f53)"},
73        { "zh_TW", "\u4e2d\u6587 (\u7e41\u9ad4)"},
74    };
75 
76    private static final String COMMAND_HISTORY = "commandHistory";
77 
78    private static final String DEFAULT_LANGUAGE = "en";
79 
80    private static final String[] GENERIC = {
81        "Generic JNDI Data Source|javax.naming.InitialContext|" +
82                "java:comp/env/jdbc/Test|sa",
83        "Generic Firebird Server|org.firebirdsql.jdbc.FBDriver|" +
84                "jdbc:firebirdsql:localhost:c:/temp/firebird/test|sysdba",
85        "Generic SQLite|org.sqlite.JDBC|" +
86                "jdbc:sqlite:test|sa",
87        "Generic DB2|COM.ibm.db2.jdbc.net.DB2Driver|" +
88                "jdbc:db2://localhost/test|" ,
89        "Generic Oracle|oracle.jdbc.driver.OracleDriver|" +
90                    "jdbc:oracle:thin:@localhost:1521:XE|sa" ,
91        "Generic MS SQL Server 2000|com.microsoft.jdbc.sqlserver.SQLServerDriver|" +
92                "jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=sqlexpress|sa",
93        "Generic MS SQL Server 2005|com.microsoft.sqlserver.jdbc.SQLServerDriver|" +
94                "jdbc:sqlserver://localhost;DatabaseName=test|sa",
95        "Generic PostgreSQL|org.postgresql.Driver|" +
96                "jdbc:postgresql:test|" ,
97        "Generic MySQL|com.mysql.jdbc.Driver|" +
98                "jdbc:mysql://localhost:3306/test|" ,
99        "Generic HSQLDB|org.hsqldb.jdbcDriver|" +
100                "jdbc:hsqldb:test;hsqldb.default_table_type=cached|sa" ,
101        "Generic Derby (Server)|org.apache.derby.jdbc.ClientDriver|" +
102                "jdbc:derby://localhost:1527/test;create=true|sa",
103        "Generic Derby (Embedded)|org.apache.derby.jdbc.EmbeddedDriver|" +
104                "jdbc:derby:test;create=true|sa",
105        "Generic H2 (Server)|org.h2.Driver|" +
106                "jdbc:h2:tcp://localhost/~/test|sa",
107        // this will be listed on top for new installations
108        "Generic H2 (Embedded)|org.h2.Driver|" +
109                "jdbc:h2:~/test|sa",
110    };
111 
112    private static int ticker;
113 
114    /**
115     * The session timeout (the default is 30 minutes).
116     */
117    private static final long SESSION_TIMEOUT = SysProperties.CONSOLE_TIMEOUT;
118 
119//    public static void main(String... args) throws IOException {
120//        String s = IOUtils.readStringAndClose(new java.io.FileReader(
121//                // "src/main/org/h2/server/web/res/_text_cs.prop"), -1);
122//                "src/main/org/h2/res/_messages_cs.prop"), -1);
123//        System.out.println(StringUtils.javaEncode("..."));
124//        String[] list = Locale.getISOLanguages();
125//        for (int i = 0; i < list.length; i++) {
126//            System.out.print(list[i] + " ");
127//        }
128//        System.out.println();
129//        String l = "de";
130//        String lang = new java.util.Locale(l).
131//            getDisplayLanguage(new java.util.Locale(l));
132//        System.out.println(new java.util.Locale(l).getDisplayLanguage());
133//        System.out.println(lang);
134//        java.util.Locale.CHINESE.getDisplayLanguage(java.util.Locale.CHINESE);
135//        for (int i = 0; i < lang.length(); i++) {
136//            System.out.println(Integer.toHexString(lang.charAt(i)) + " ");
137//        }
138//    }
139 
140    // private URLClassLoader urlClassLoader;
141    private int port;
142    private boolean allowOthers;
143    private boolean isDaemon;
144    private final Set<WebThread> running =
145            Collections.synchronizedSet(new HashSet<WebThread>());
146    private boolean ssl;
147    private final HashMap<String, ConnectionInfo> connInfoMap = New.hashMap();
148 
149    private long lastTimeoutCheck;
150    private final HashMap<String, WebSession> sessions = New.hashMap();
151    private final HashSet<String> languages = New.hashSet();
152    private String startDateTime;
153    private ServerSocket serverSocket;
154    private String url;
155    private ShutdownHandler shutdownHandler;
156    private Thread listenerThread;
157    private boolean ifExists;
158    private boolean trace;
159    private TranslateThread translateThread;
160    private boolean allowChunked = true;
161    private String serverPropertiesDir = Constants.SERVER_PROPERTIES_DIR;
162    // null means the history is not allowed to be stored
163    private String commandHistoryString;
164 
165    /**
166     * Read the given file from the file system or from the resources.
167     *
168     * @param file the file name
169     * @return the data
170     */
171    byte[] getFile(String file) throws IOException {
172        trace("getFile <" + file + ">");
173        if (file.startsWith(TRANSFER + "/") && new File(TRANSFER).exists()) {
174            file = file.substring(TRANSFER.length() + 1);
175            if (!isSimpleName(file)) {
176                return null;
177            }
178            File f = new File(TRANSFER, file);
179            if (!f.exists()) {
180                return null;
181            }
182            return IOUtils.readBytesAndClose(new FileInputStream(f), -1);
183        }
184        byte[] data = Utils.getResource("/org/h2/server/web/res/" + file);
185        if (data == null) {
186            trace(" null");
187        } else {
188            trace(" size=" + data.length);
189        }
190        return data;
191    }
192 
193    /**
194     * Check if this is a simple name (only contains '.', '-', '_', letters, or
195     * digits).
196     *
197     * @param s the string
198     * @return true if it's a simple name
199     */
200    static boolean isSimpleName(String s) {
201        for (char c : s.toCharArray()) {
202            if (c != '.' && c != '_' && c != '-' && !Character.isLetterOrDigit(c)) {
203                return false;
204            }
205        }
206        return true;
207    }
208 
209    /**
210     * Remove this web thread from the set of running threads.
211     *
212     * @param t the thread to remove
213     */
214    synchronized void remove(WebThread t) {
215        running.remove(t);
216    }
217 
218    private static String generateSessionId() {
219        byte[] buff = MathUtils.secureRandomBytes(16);
220        return StringUtils.convertBytesToHex(buff);
221    }
222 
223    /**
224     * Get the web session object for the given session id.
225     *
226     * @param sessionId the session id
227     * @return the web session or null
228     */
229    WebSession getSession(String sessionId) {
230        long now = System.currentTimeMillis();
231        if (lastTimeoutCheck + SESSION_TIMEOUT < now) {
232            for (String id : New.arrayList(sessions.keySet())) {
233                WebSession session = sessions.get(id);
234                if (session.lastAccess + SESSION_TIMEOUT < now) {
235                    trace("timeout for " + id);
236                    sessions.remove(id);
237                }
238            }
239            lastTimeoutCheck = now;
240        }
241        WebSession session = sessions.get(sessionId);
242        if (session != null) {
243            session.lastAccess = System.currentTimeMillis();
244        }
245        return session;
246    }
247 
248    /**
249     * Create a new web session id and object.
250     *
251     * @param hostAddr the host address
252     * @return the web session object
253     */
254    WebSession createNewSession(String hostAddr) {
255        String newId;
256        do {
257            newId = generateSessionId();
258        } while (sessions.get(newId) != null);
259        WebSession session = new WebSession(this);
260        session.lastAccess = System.currentTimeMillis();
261        session.put("sessionId", newId);
262        session.put("ip", hostAddr);
263        session.put("language", DEFAULT_LANGUAGE);
264        session.put("frame-border", "0");
265        session.put("frameset-border", "4");
266        sessions.put(newId, session);
267        // always read the english translation,
268        // so that untranslated text appears at least in english
269        readTranslations(session, DEFAULT_LANGUAGE);
270        return getSession(newId);
271    }
272 
273    String getStartDateTime() {
274        if (startDateTime == null) {
275            SimpleDateFormat format = new SimpleDateFormat(
276                    "EEE, d MMM yyyy HH:mm:ss z", new Locale("en", ""));
277            format.setTimeZone(TimeZone.getTimeZone("GMT"));
278            startDateTime = format.format(System.currentTimeMillis());
279        }
280        return startDateTime;
281    }
282 
283    @Override
284    public void init(String... args) {
285        // set the serverPropertiesDir, because it's used in loadProperties()
286        for (int i = 0; args != null && i < args.length; i++) {
287            if ("-properties".equals(args[i])) {
288                serverPropertiesDir = args[++i];
289            }
290        }
291        Properties prop = loadProperties();
292        port = SortedProperties.getIntProperty(prop,
293                "webPort", Constants.DEFAULT_HTTP_PORT);
294        ssl = SortedProperties.getBooleanProperty(prop,
295                "webSSL", false);
296        allowOthers = SortedProperties.getBooleanProperty(prop,
297                "webAllowOthers", false);
298        commandHistoryString = prop.getProperty(COMMAND_HISTORY);
299        for (int i = 0; args != null && i < args.length; i++) {
300            String a = args[i];
301            if (Tool.isOption(a, "-webPort")) {
302                port = Integer.decode(args[++i]);
303            } else if (Tool.isOption(a, "-webSSL")) {
304                ssl = true;
305            } else if (Tool.isOption(a, "-webAllowOthers")) {
306                allowOthers = true;
307            } else if (Tool.isOption(a, "-webDaemon")) {
308                isDaemon = true;
309            } else if (Tool.isOption(a, "-baseDir")) {
310                String baseDir = args[++i];
311                SysProperties.setBaseDir(baseDir);
312            } else if (Tool.isOption(a, "-ifExists")) {
313                ifExists = true;
314            } else if (Tool.isOption(a, "-properties")) {
315                // already set
316                i++;
317            } else if (Tool.isOption(a, "-trace")) {
318                trace = true;
319            }
320        }
321//            if (driverList != null) {
322//                try {
323//                    String[] drivers =
324//                        StringUtils.arraySplit(driverList, ',', false);
325//                    URL[] urls = new URL[drivers.length];
326//                    for(int i=0; i<drivers.length; i++) {
327//                        urls[i] = new URL(drivers[i]);
328//                    }
329//                    urlClassLoader = URLClassLoader.newInstance(urls);
330//                } catch (MalformedURLException e) {
331//                    TraceSystem.traceThrowable(e);
332//                }
333//            }
334        for (String[] lang : LANGUAGES) {
335            languages.add(lang[0]);
336        }
337        updateURL();
338    }
339 
340    @Override
341    public String getURL() {
342        updateURL();
343        return url;
344    }
345 
346    private void updateURL() {
347        try {
348            url = (ssl ? "https" : "http") + "://" +
349                    NetUtils.getLocalAddress() + ":" + port;
350        } catch (NoClassDefFoundError e) {
351            // Google App Engine does not allow java.net.InetAddress
352        }
353    }
354 
355    @Override
356    public void start() {
357        serverSocket = NetUtils.createServerSocket(port, ssl);
358        port = serverSocket.getLocalPort();
359        updateURL();
360    }
361 
362    @Override
363    public void listen() {
364        this.listenerThread = Thread.currentThread();
365        try {
366            while (serverSocket != null) {
367                Socket s = serverSocket.accept();
368                WebThread c = new WebThread(s, this);
369                running.add(c);
370                c.start();
371            }
372        } catch (Exception e) {
373            trace(e.toString());
374        }
375    }
376 
377    @Override
378    public boolean isRunning(boolean traceError) {
379        if (serverSocket == null) {
380            return false;
381        }
382        try {
383            Socket s = NetUtils.createLoopbackSocket(port, ssl);
384            s.close();
385            return true;
386        } catch (Exception e) {
387            if (traceError) {
388                traceError(e);
389            }
390            return false;
391        }
392    }
393 
394    public boolean isStopped() {
395        return serverSocket == null;
396    }
397 
398    @Override
399    public void stop() {
400        if (serverSocket != null) {
401            try {
402                serverSocket.close();
403            } catch (IOException e) {
404                traceError(e);
405            }
406            serverSocket = null;
407        }
408        if (listenerThread != null) {
409            try {
410                listenerThread.join(1000);
411            } catch (InterruptedException e) {
412                DbException.traceThrowable(e);
413            }
414        }
415        // TODO server: using a boolean 'now' argument? a timeout?
416        for (WebSession session : New.arrayList(sessions.values())) {
417            session.close();
418        }
419        for (WebThread c : New.arrayList(running)) {
420            try {
421                c.stopNow();
422                c.join(100);
423            } catch (Exception e) {
424                traceError(e);
425            }
426        }
427    }
428 
429    /**
430     * Write trace information if trace is enabled.
431     *
432     * @param s the message to write
433     */
434    void trace(String s) {
435        if (trace) {
436            System.out.println(s);
437        }
438    }
439 
440    /**
441     * Write the stack trace if trace is enabled.
442     *
443     * @param e the exception
444     */
445    void traceError(Throwable e) {
446        if (trace) {
447            e.printStackTrace();
448        }
449    }
450 
451    /**
452     * Check if this language is supported / translated.
453     *
454     * @param language the language
455     * @return true if a translation is available
456     */
457    boolean supportsLanguage(String language) {
458        return languages.contains(language);
459    }
460 
461    /**
462     * Read the translation for this language and save them in the 'text'
463     * property of this session.
464     *
465     * @param session the session
466     * @param language the language
467     */
468    void readTranslations(WebSession session, String language) {
469        Properties text = new Properties();
470        try {
471            trace("translation: "+language);
472            byte[] trans = getFile("_text_"+language+".prop");
473            trace("  "+new String(trans));
474            text = SortedProperties.fromLines(new String(trans, Constants.UTF8));
475            // remove starting # (if not translated yet)
476            for (Entry<Object, Object> entry : text.entrySet()) {
477                String value = (String) entry.getValue();
478                if (value.startsWith("#")) {
479                    entry.setValue(value.substring(1));
480                }
481            }
482        } catch (IOException e) {
483            DbException.traceThrowable(e);
484        }
485        session.put("text", new HashMap<Object, Object>(text));
486    }
487 
488    ArrayList<HashMap<String, Object>> getSessions() {
489        ArrayList<HashMap<String, Object>> list = New.arrayList();
490        for (WebSession s : sessions.values()) {
491            list.add(s.getInfo());
492        }
493        return list;
494    }
495 
496    @Override
497    public String getType() {
498        return "Web Console";
499    }
500 
501    @Override
502    public String getName() {
503        return "H2 Console Server";
504    }
505 
506    void setAllowOthers(boolean b) {
507        allowOthers = b;
508    }
509 
510    @Override
511    public boolean getAllowOthers() {
512        return allowOthers;
513    }
514 
515    void setSSL(boolean b) {
516        ssl = b;
517    }
518 
519    void setPort(int port) {
520        this.port = port;
521    }
522 
523    boolean getSSL() {
524        return ssl;
525    }
526 
527    @Override
528    public int getPort() {
529        return port;
530    }
531 
532    public boolean isCommandHistoryAllowed() {
533        return commandHistoryString != null;
534    }
535 
536    public void setCommandHistoryAllowed(boolean allowed) {
537        if (allowed) {
538            if (commandHistoryString == null) {
539                commandHistoryString = "";
540            }
541        } else {
542            commandHistoryString = null;
543        }
544    }
545 
546    public ArrayList<String> getCommandHistoryList() {
547        ArrayList<String> result = New.arrayList();
548        if (commandHistoryString == null) {
549            return result;
550        }
551 
552        // Split the commandHistoryString on non-escaped semicolons
553        // and unescape it.
554        StringBuilder sb = new StringBuilder();
555        for (int end = 0;; end++) {
556            if (end == commandHistoryString.length() ||
557                    commandHistoryString.charAt(end) == ';') {
558                if (sb.length() > 0) {
559                    result.add(sb.toString());
560                    sb.delete(0, sb.length());
561                }
562                if (end == commandHistoryString.length()) {
563                    break;
564                }
565            } else if (commandHistoryString.charAt(end) == '\\' &&
566                    end < commandHistoryString.length() - 1) {
567                sb.append(commandHistoryString.charAt(++end));
568            } else {
569                sb.append(commandHistoryString.charAt(end));
570            }
571        }
572        return result;
573    }
574 
575    /**
576     * Save the command history to the properties file.
577     *
578     * @param commandHistory the history
579     */
580    public void saveCommandHistoryList(ArrayList<String> commandHistory) {
581        StringBuilder sb = new StringBuilder();
582        for (String s : commandHistory) {
583            if (sb.length() > 0) {
584                sb.append(';');
585            }
586            sb.append(s.replace("\\", "\\\\").replace(";", "\\;"));
587        }
588        commandHistoryString = sb.toString();
589        saveProperties(null);
590    }
591 
592    /**
593     * Get the connection information for this setting.
594     *
595     * @param name the setting name
596     * @return the connection information
597     */
598    ConnectionInfo getSetting(String name) {
599        return connInfoMap.get(name);
600    }
601 
602    /**
603     * Update a connection information setting.
604     *
605     * @param info the connection information
606     */
607    void updateSetting(ConnectionInfo info) {
608        connInfoMap.put(info.name, info);
609        info.lastAccess = ticker++;
610    }
611 
612    /**
613     * Remove a connection information setting from the list
614     *
615     * @param name the setting to remove
616     */
617    void removeSetting(String name) {
618        connInfoMap.remove(name);
619    }
620 
621    private Properties loadProperties() {
622        try {
623            if ("null".equals(serverPropertiesDir)) {
624                return new Properties();
625            }
626            return SortedProperties.loadProperties(
627                    serverPropertiesDir + "/" + Constants.SERVER_PROPERTIES_NAME);
628        } catch (Exception e) {
629            DbException.traceThrowable(e);
630            return new Properties();
631        }
632    }
633 
634    /**
635     * Get the list of connection information setting names.
636     *
637     * @return the connection info names
638     */
639    String[] getSettingNames() {
640        ArrayList<ConnectionInfo> list = getSettings();
641        String[] names = new String[list.size()];
642        for (int i = 0; i < list.size(); i++) {
643            names[i] = list.get(i).name;
644        }
645        return names;
646    }
647 
648    /**
649     * Get the list of connection info objects.
650     *
651     * @return the list
652     */
653    synchronized ArrayList<ConnectionInfo> getSettings() {
654        ArrayList<ConnectionInfo> settings = New.arrayList();
655        if (connInfoMap.size() == 0) {
656            Properties prop = loadProperties();
657            if (prop.size() == 0) {
658                for (String gen : GENERIC) {
659                    ConnectionInfo info = new ConnectionInfo(gen);
660                    settings.add(info);
661                    updateSetting(info);
662                }
663            } else {
664                for (int i = 0;; i++) {
665                    String data = prop.getProperty(String.valueOf(i));
666                    if (data == null) {
667                        break;
668                    }
669                    ConnectionInfo info = new ConnectionInfo(data);
670                    settings.add(info);
671                    updateSetting(info);
672                }
673            }
674        } else {
675            settings.addAll(connInfoMap.values());
676        }
677        Collections.sort(settings);
678        return settings;
679    }
680 
681    /**
682     * Save the settings to the properties file.
683     *
684     * @param prop null or the properties webPort, webAllowOthers, and webSSL
685     */
686    synchronized void saveProperties(Properties prop) {
687        try {
688            if (prop == null) {
689                Properties old = loadProperties();
690                prop = new SortedProperties();
691                prop.setProperty("webPort",
692                        "" + SortedProperties.getIntProperty(old,
693                        "webPort", port));
694                prop.setProperty("webAllowOthers",
695                        "" + SortedProperties.getBooleanProperty(old,
696                        "webAllowOthers", allowOthers));
697                prop.setProperty("webSSL",
698                        "" + SortedProperties.getBooleanProperty(old,
699                        "webSSL", ssl));
700                if (commandHistoryString != null) {
701                    prop.setProperty(COMMAND_HISTORY, commandHistoryString);
702                }
703            }
704            ArrayList<ConnectionInfo> settings = getSettings();
705            int len = settings.size();
706            for (int i = 0; i < len; i++) {
707                ConnectionInfo info = settings.get(i);
708                if (info != null) {
709                    prop.setProperty(String.valueOf(len - i - 1), info.getString());
710                }
711            }
712            if (!"null".equals(serverPropertiesDir)) {
713                OutputStream out = FileUtils.newOutputStream(
714                        serverPropertiesDir + "/" + Constants.SERVER_PROPERTIES_NAME, false);
715                prop.store(out, "H2 Server Properties");
716                out.close();
717            }
718        } catch (Exception e) {
719            DbException.traceThrowable(e);
720        }
721    }
722 
723    /**
724     * Open a database connection.
725     *
726     * @param driver the driver class name
727     * @param databaseUrl the database URL
728     * @param user the user name
729     * @param password the password
730     * @return the database connection
731     */
732    Connection getConnection(String driver, String databaseUrl, String user,
733            String password) throws SQLException {
734        driver = driver.trim();
735        databaseUrl = databaseUrl.trim();
736        org.h2.Driver.load();
737        Properties p = new Properties();
738        p.setProperty("user", user.trim());
739        // do not trim the password, otherwise an
740        // encrypted H2 database with empty user password doesn't work
741        p.setProperty("password", password);
742        if (databaseUrl.startsWith("jdbc:h2:")) {
743            if (ifExists) {
744                databaseUrl += ";IFEXISTS=TRUE";
745            }
746            // PostgreSQL would throw a NullPointerException
747            // if it is loaded before the H2 driver
748            // because it can't deal with non-String objects in the connection
749            // Properties
750            return org.h2.Driver.load().connect(databaseUrl, p);
751        }
752//            try {
753//                Driver dr = (Driver) urlClassLoader.
754//                        loadClass(driver).newInstance();
755//                return dr.connect(url, p);
756//            } catch(ClassNotFoundException e2) {
757//                throw e2;
758//            }
759        return JdbcUtils.getConnection(driver, databaseUrl, p);
760    }
761 
762    /**
763     * Shut down the web server.
764     */
765    void shutdown() {
766        if (shutdownHandler != null) {
767            shutdownHandler.shutdown();
768        }
769    }
770 
771    public void setShutdownHandler(ShutdownHandler shutdownHandler) {
772        this.shutdownHandler = shutdownHandler;
773    }
774 
775    /**
776     * Create a session with a given connection.
777     *
778     * @param conn the connection
779     * @return the URL of the web site to access this connection
780     */
781    public String addSession(Connection conn) throws SQLException {
782        WebSession session = createNewSession("local");
783        session.setShutdownServerOnDisconnect();
784        session.setConnection(conn);
785        session.put("url", conn.getMetaData().getURL());
786        String s = (String) session.get("sessionId");
787        return url + "/frame.jsp?jsessionid=" + s;
788    }
789 
790    /**
791     * The translate thread reads and writes the file translation.properties
792     * once a second.
793     */
794    private class TranslateThread extends Thread {
795 
796        private final File file = new File("translation.properties");
797        private final Map<Object, Object> translation;
798        private volatile boolean stopNow;
799 
800        TranslateThread(Map<Object, Object> translation) {
801            this.translation = translation;
802        }
803 
804        public String getFileName() {
805            return file.getAbsolutePath();
806        }
807 
808        public void stopNow() {
809            this.stopNow = true;
810            try {
811                join();
812            } catch (InterruptedException e) {
813                // ignore
814            }
815        }
816 
817        @Override
818        public void run() {
819            while (!stopNow) {
820                try {
821                    SortedProperties sp = new SortedProperties();
822                    if (file.exists()) {
823                        InputStream in = FileUtils.newInputStream(file.getName());
824                        sp.load(in);
825                        translation.putAll(sp);
826                    } else {
827                        OutputStream out = FileUtils.newOutputStream(file.getName(), false);
828                        sp.putAll(translation);
829                        sp.store(out, "Translation");
830                    }
831                    Thread.sleep(1000);
832                } catch (Exception e) {
833                    traceError(e);
834                }
835            }
836        }
837 
838    }
839 
840    /**
841     * Start the translation thread that reads the file once a second.
842     *
843     * @param translation the translation map
844     * @return the name of the file to translate
845     */
846    String startTranslate(Map<Object, Object> translation) {
847        if (translateThread != null) {
848            translateThread.stopNow();
849        }
850        translateThread = new TranslateThread(translation);
851        translateThread.setDaemon(true);
852        translateThread.start();
853        return translateThread.getFileName();
854    }
855 
856    @Override
857    public boolean isDaemon() {
858        return isDaemon;
859    }
860 
861    void setAllowChunked(boolean allowChunked) {
862        this.allowChunked = allowChunked;
863    }
864 
865    boolean getAllowChunked() {
866        return allowChunked;
867    }
868 
869}

[all classes][org.h2.server.web]
EMMA 2.0.5312 (C) Vladimir Roubtsov