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 | */ |
6 | package org.h2.server.web; |
7 | |
8 | import java.sql.Connection; |
9 | import java.sql.DatabaseMetaData; |
10 | import java.sql.ResultSet; |
11 | import java.sql.SQLException; |
12 | import java.sql.Statement; |
13 | import java.sql.Timestamp; |
14 | import java.util.ArrayList; |
15 | import java.util.HashMap; |
16 | import java.util.Locale; |
17 | |
18 | import org.h2.bnf.Bnf; |
19 | import org.h2.bnf.context.DbContents; |
20 | import org.h2.bnf.context.DbContextRule; |
21 | import org.h2.message.DbException; |
22 | import org.h2.util.New; |
23 | |
24 | /** |
25 | * The web session keeps all data of a user session. |
26 | * This class is used by the H2 Console. |
27 | */ |
28 | class WebSession { |
29 | |
30 | private static final int MAX_HISTORY = 1000; |
31 | |
32 | /** |
33 | * The last time this client sent a request. |
34 | */ |
35 | long lastAccess; |
36 | |
37 | /** |
38 | * The session attribute map. |
39 | */ |
40 | final HashMap<String, Object> map = New.hashMap(); |
41 | |
42 | /** |
43 | * The current locale. |
44 | */ |
45 | Locale locale; |
46 | |
47 | /** |
48 | * The currently executing statement. |
49 | */ |
50 | Statement executingStatement; |
51 | |
52 | /** |
53 | * The current updatable result set. |
54 | */ |
55 | ResultSet result; |
56 | |
57 | private final WebServer server; |
58 | |
59 | private final ArrayList<String> commandHistory; |
60 | |
61 | private Connection conn; |
62 | private DatabaseMetaData meta; |
63 | private DbContents contents = new DbContents(); |
64 | private Bnf bnf; |
65 | private boolean shutdownServerOnDisconnect; |
66 | |
67 | WebSession(WebServer server) { |
68 | this.server = server; |
69 | // This must be stored in the session rather than in the server. |
70 | // Otherwise, one client could allow |
71 | // saving history for others (insecure). |
72 | this.commandHistory = server.getCommandHistoryList(); |
73 | } |
74 | |
75 | /** |
76 | * Put an attribute value in the map. |
77 | * |
78 | * @param key the key |
79 | * @param value the new value |
80 | */ |
81 | void put(String key, Object value) { |
82 | map.put(key, value); |
83 | } |
84 | |
85 | /** |
86 | * Get the value for the given key. |
87 | * |
88 | * @param key the key |
89 | * @return the value |
90 | */ |
91 | Object get(String key) { |
92 | if ("sessions".equals(key)) { |
93 | return server.getSessions(); |
94 | } |
95 | return map.get(key); |
96 | } |
97 | |
98 | /** |
99 | * Remove a session attribute from the map. |
100 | * |
101 | * @param key the key |
102 | */ |
103 | void remove(String key) { |
104 | map.remove(key); |
105 | } |
106 | |
107 | /** |
108 | * Get the BNF object. |
109 | * |
110 | * @return the BNF object |
111 | */ |
112 | Bnf getBnf() { |
113 | return bnf; |
114 | } |
115 | |
116 | /** |
117 | * Load the SQL grammar BNF. |
118 | */ |
119 | void loadBnf() { |
120 | try { |
121 | Bnf newBnf = Bnf.getInstance(null); |
122 | DbContextRule columnRule = |
123 | new DbContextRule(contents, DbContextRule.COLUMN); |
124 | DbContextRule newAliasRule = |
125 | new DbContextRule(contents, DbContextRule.NEW_TABLE_ALIAS); |
126 | DbContextRule aliasRule = |
127 | new DbContextRule(contents, DbContextRule.TABLE_ALIAS); |
128 | DbContextRule tableRule = |
129 | new DbContextRule(contents, DbContextRule.TABLE); |
130 | DbContextRule schemaRule = |
131 | new DbContextRule(contents, DbContextRule.SCHEMA); |
132 | DbContextRule columnAliasRule = |
133 | new DbContextRule(contents, DbContextRule.COLUMN_ALIAS); |
134 | newBnf.updateTopic("column_name", columnRule); |
135 | newBnf.updateTopic("new_table_alias", newAliasRule); |
136 | newBnf.updateTopic("table_alias", aliasRule); |
137 | newBnf.updateTopic("column_alias", columnAliasRule); |
138 | newBnf.updateTopic("table_name", tableRule); |
139 | newBnf.updateTopic("schema_name", schemaRule); |
140 | newBnf.linkStatements(); |
141 | bnf = newBnf; |
142 | } catch (Exception e) { |
143 | // ok we don't have the bnf |
144 | server.traceError(e); |
145 | } |
146 | } |
147 | |
148 | /** |
149 | * Get the SQL statement from history. |
150 | * |
151 | * @param id the history id |
152 | * @return the SQL statement |
153 | */ |
154 | String getCommand(int id) { |
155 | return commandHistory.get(id); |
156 | } |
157 | |
158 | /** |
159 | * Add a SQL statement to the history. |
160 | * |
161 | * @param sql the SQL statement |
162 | */ |
163 | void addCommand(String sql) { |
164 | if (sql == null) { |
165 | return; |
166 | } |
167 | sql = sql.trim(); |
168 | if (sql.length() == 0) { |
169 | return; |
170 | } |
171 | if (commandHistory.size() > MAX_HISTORY) { |
172 | commandHistory.remove(0); |
173 | } |
174 | int idx = commandHistory.indexOf(sql); |
175 | if (idx >= 0) { |
176 | commandHistory.remove(idx); |
177 | } |
178 | commandHistory.add(sql); |
179 | if (server.isCommandHistoryAllowed()) { |
180 | server.saveCommandHistoryList(commandHistory); |
181 | } |
182 | } |
183 | |
184 | /** |
185 | * Get the list of SQL statements in the history. |
186 | * |
187 | * @return the commands |
188 | */ |
189 | ArrayList<String> getCommandHistory() { |
190 | return commandHistory; |
191 | } |
192 | |
193 | /** |
194 | * Update session meta data information and get the information in a map. |
195 | * |
196 | * @return a map containing the session meta data |
197 | */ |
198 | HashMap<String, Object> getInfo() { |
199 | HashMap<String, Object> m = New.hashMap(); |
200 | m.putAll(map); |
201 | m.put("lastAccess", new Timestamp(lastAccess).toString()); |
202 | try { |
203 | m.put("url", conn == null ? |
204 | "${text.admin.notConnected}" : conn.getMetaData().getURL()); |
205 | m.put("user", conn == null ? |
206 | "-" : conn.getMetaData().getUserName()); |
207 | m.put("lastQuery", commandHistory.size() == 0 ? |
208 | "" : commandHistory.get(0)); |
209 | m.put("executing", executingStatement == null ? |
210 | "${text.admin.no}" : "${text.admin.yes}"); |
211 | } catch (SQLException e) { |
212 | DbException.traceThrowable(e); |
213 | } |
214 | return m; |
215 | } |
216 | |
217 | void setConnection(Connection conn) throws SQLException { |
218 | this.conn = conn; |
219 | if (conn == null) { |
220 | meta = null; |
221 | } else { |
222 | meta = conn.getMetaData(); |
223 | } |
224 | contents = new DbContents(); |
225 | } |
226 | |
227 | DatabaseMetaData getMetaData() { |
228 | return meta; |
229 | } |
230 | |
231 | Connection getConnection() { |
232 | return conn; |
233 | } |
234 | |
235 | DbContents getContents() { |
236 | return contents; |
237 | } |
238 | |
239 | /** |
240 | * Shutdown the server when disconnecting. |
241 | */ |
242 | void setShutdownServerOnDisconnect() { |
243 | this.shutdownServerOnDisconnect = true; |
244 | } |
245 | |
246 | boolean getShutdownServerOnDisconnect() { |
247 | return shutdownServerOnDisconnect; |
248 | } |
249 | |
250 | /** |
251 | * Close the connection and stop the statement if one is currently |
252 | * executing. |
253 | */ |
254 | void close() { |
255 | if (executingStatement != null) { |
256 | try { |
257 | executingStatement.cancel(); |
258 | } catch (Exception e) { |
259 | // ignore |
260 | } |
261 | } |
262 | if (conn != null) { |
263 | try { |
264 | conn.close(); |
265 | } catch (Exception e) { |
266 | // ignore |
267 | } |
268 | } |
269 | |
270 | } |
271 | |
272 | } |