simplify H2 console, and deploy it only when in Integration project stage
Showing
7 changed files
with
22 additions
and
3944 deletions
1 | -package org.legrog.util; | ||
2 | -// Code copied from org.h2.server.web.ConnectionInfo | ||
3 | -/* | ||
4 | - * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0, | ||
5 | - * and the EPL 1.0 (http://h2database.com/html/license.html). | ||
6 | - * Initial Developer: H2 Group | ||
7 | - */ | ||
8 | -//package org.h2.server.web; | ||
9 | - | ||
10 | - import org.h2.util.MathUtils; | ||
11 | - import org.h2.util.StringUtils; | ||
12 | - | ||
13 | -/** | ||
14 | - * The connection info object is a wrapper for database connection information | ||
15 | - * such as the database URL, user name and password. | ||
16 | - * This class is used by the H2 Console. | ||
17 | - */ | ||
18 | -public class ConnectionInfo implements Comparable<ConnectionInfo> { | ||
19 | - /** | ||
20 | - * The driver class name. | ||
21 | - */ | ||
22 | - public String driver; | ||
23 | - | ||
24 | - /** | ||
25 | - * The database URL. | ||
26 | - */ | ||
27 | - public String url; | ||
28 | - | ||
29 | - /** | ||
30 | - * The user name. | ||
31 | - */ | ||
32 | - public String user; | ||
33 | - | ||
34 | - /** | ||
35 | - * The connection display name. | ||
36 | - */ | ||
37 | - String name; | ||
38 | - | ||
39 | - /** | ||
40 | - * The last time this connection was used. | ||
41 | - */ | ||
42 | - int lastAccess; | ||
43 | - | ||
44 | - ConnectionInfo() { | ||
45 | - // nothing to do | ||
46 | - } | ||
47 | - | ||
48 | - public ConnectionInfo(String data) { | ||
49 | - String[] array = StringUtils.arraySplit(data, '|', false); | ||
50 | - name = get(array, 0); | ||
51 | - driver = get(array, 1); | ||
52 | - url = get(array, 2); | ||
53 | - user = get(array, 3); | ||
54 | - } | ||
55 | - | ||
56 | - private static String get(String[] array, int i) { | ||
57 | - return array != null && array.length > i ? array[i] : ""; | ||
58 | - } | ||
59 | - | ||
60 | - String getString() { | ||
61 | - return StringUtils.arrayCombine(new String[] { name, driver, url, user }, '|'); | ||
62 | - } | ||
63 | - | ||
64 | - @Override | ||
65 | - public int compareTo(ConnectionInfo o) { | ||
66 | - return -MathUtils.compareInt(lastAccess, o.lastAccess); | ||
67 | - } | ||
68 | - | ||
69 | -} | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
1 | package org.legrog.util; | 1 | package org.legrog.util; |
2 | 2 | ||
3 | -import javax.servlet.annotation.WebServlet; | 3 | +import org.apache.deltaspike.core.api.projectstage.ProjectStage; |
4 | - | 4 | +import org.apache.deltaspike.core.util.ProjectStageProducer; |
5 | -import java.io.IOException; | 5 | +import org.slf4j.Logger; |
6 | -import java.net.InetAddress; | 6 | +import org.slf4j.LoggerFactory; |
7 | -import java.net.UnknownHostException; | ||
8 | -import java.util.ArrayList; | ||
9 | -import java.util.Enumeration; | ||
10 | -import java.util.Properties; | ||
11 | 7 | ||
12 | -import javax.servlet.ServletConfig; | 8 | +import javax.servlet.annotation.WebInitParam; |
13 | -import javax.servlet.ServletOutputStream; | 9 | +import javax.servlet.annotation.WebServlet; |
14 | -import javax.servlet.http.HttpServlet; | ||
15 | -import javax.servlet.http.HttpServletRequest; | ||
16 | -import javax.servlet.http.HttpServletResponse; | ||
17 | 10 | ||
18 | -import org.h2.engine.Constants; | 11 | +import static org.legrog.util.H2ConsoleServlet.CONSOLE_MAPPING; |
19 | -import org.h2.server.web.PageParser; | ||
20 | -import org.h2.util.New; | ||
21 | 12 | ||
22 | 13 | ||
23 | -@WebServlet(urlPatterns="/console/*", name="H2Console", loadOnStartup=1) | 14 | +@WebServlet( |
24 | -public class H2ConsoleServlet extends HttpServlet { | 15 | + urlPatterns = CONSOLE_MAPPING, |
25 | -// Code copied from org.h2.server.web.WebServlet | 16 | + name = "H2Console", |
26 | - private static final long serialVersionUID = 1L; | 17 | + loadOnStartup = 1, |
27 | - private transient WebServer server; | 18 | + initParams = {@WebInitParam(name = "webAllowOthers", value = ""), @WebInitParam(name = "trace", value = "")} |
19 | +) | ||
20 | +public class H2ConsoleServlet extends org.h2.server.web.WebServlet { | ||
21 | + static final String CONSOLE_MAPPING = "/console/*"; | ||
22 | + private Logger logger = LoggerFactory.getLogger(getClass()); | ||
28 | 23 | ||
29 | @Override | 24 | @Override |
30 | public void init() { | 25 | public void init() { |
31 | - ServletConfig config = getServletConfig(); | 26 | + ProjectStage projectStage = ProjectStageProducer.getInstance().getProjectStage(); |
32 | - Enumeration<?> en = config.getInitParameterNames(); | 27 | + if (projectStage == ProjectStage.IntegrationTest) { |
33 | - ArrayList<String> list = New.arrayList(); | 28 | + logger.info("Create a H2 Console Servlet mapped to {}", CONSOLE_MAPPING); |
34 | - while (en.hasMoreElements()) { | 29 | + super.init(); |
35 | - String name = en.nextElement().toString(); | ||
36 | - String value = config.getInitParameter(name); | ||
37 | - if (!name.startsWith("-")) { | ||
38 | - name = "-" + name; | ||
39 | - } | ||
40 | - list.add(name); | ||
41 | - if (value.length() > 0) { | ||
42 | - list.add(value); | ||
43 | - } | ||
44 | - } | ||
45 | - String[] args = new String[list.size()]; | ||
46 | - list.toArray(args); | ||
47 | - server = new WebServer(); | ||
48 | - server.setAllowChunked(false); | ||
49 | - server.init(args); | ||
50 | - } | ||
51 | - | ||
52 | - @Override | ||
53 | - public void destroy() { | ||
54 | - server.stop(); | ||
55 | - } | ||
56 | - | ||
57 | - private boolean allow(HttpServletRequest req) { | ||
58 | - if (server.getAllowOthers()) { | ||
59 | - return true; | ||
60 | - } | ||
61 | - String addr = req.getRemoteAddr(); | ||
62 | - try { | ||
63 | - InetAddress address = InetAddress.getByName(addr); | ||
64 | - return address.isLoopbackAddress(); | ||
65 | - } catch (UnknownHostException e) { | ||
66 | - return false; | ||
67 | - } catch (NoClassDefFoundError e) { | ||
68 | - // Google App Engine does not allow java.net.InetAddress | ||
69 | - return false; | ||
70 | - } | ||
71 | - } | ||
72 | - | ||
73 | - private String getAllowedFile(HttpServletRequest req, String requestedFile) { | ||
74 | - if (!allow(req)) { | ||
75 | - return "notAllowed.jsp"; | ||
76 | - } | ||
77 | - if (requestedFile.length() == 0) { | ||
78 | - return "index.do"; | ||
79 | - } | ||
80 | - return requestedFile; | ||
81 | - } | ||
82 | - | ||
83 | - @Override | ||
84 | - public void doGet(HttpServletRequest req, HttpServletResponse resp) | ||
85 | - throws IOException { | ||
86 | - req.setCharacterEncoding("utf-8"); | ||
87 | - String file = req.getPathInfo(); | ||
88 | - if (file == null) { | ||
89 | - resp.sendRedirect(req.getRequestURI() + "/"); | ||
90 | - return; | ||
91 | - } else if (file.startsWith("/")) { | ||
92 | - file = file.substring(1); | ||
93 | - } | ||
94 | - file = getAllowedFile(req, file); | ||
95 | - | ||
96 | - // extract the request attributes | ||
97 | - Properties attributes = new Properties(); | ||
98 | - Enumeration<?> en = req.getAttributeNames(); | ||
99 | - while (en.hasMoreElements()) { | ||
100 | - String name = en.nextElement().toString(); | ||
101 | - String value = req.getAttribute(name).toString(); | ||
102 | - attributes.put(name, value); | ||
103 | - } | ||
104 | - en = req.getParameterNames(); | ||
105 | - while (en.hasMoreElements()) { | ||
106 | - String name = en.nextElement().toString(); | ||
107 | - String value = req.getParameter(name); | ||
108 | - attributes.put(name, value); | ||
109 | - } | ||
110 | - | ||
111 | - WebSession session = null; | ||
112 | - String sessionId = attributes.getProperty("jsessionid"); | ||
113 | - if (sessionId != null) { | ||
114 | - session = server.getSession(sessionId); | ||
115 | - } | ||
116 | - WebApp app = new WebApp(server); | ||
117 | - app.setSession(session, attributes); | ||
118 | - String ifModifiedSince = req.getHeader("if-modified-since"); | ||
119 | - | ||
120 | - String hostAddr = req.getRemoteAddr(); | ||
121 | - file = app.processRequest(file, hostAddr); | ||
122 | - session = app.getSession(); | ||
123 | - | ||
124 | - String mimeType = app.getMimeType(); | ||
125 | - boolean cache = app.getCache(); | ||
126 | - | ||
127 | - if (cache && server.getStartDateTime().equals(ifModifiedSince)) { | ||
128 | - resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED); | ||
129 | - return; | ||
130 | - } | ||
131 | - byte[] bytes = server.getFile(file); | ||
132 | - if (bytes == null) { | ||
133 | - resp.sendError(HttpServletResponse.SC_NOT_FOUND); | ||
134 | - bytes = ("File not found: " + file).getBytes(Constants.UTF8); | ||
135 | } else { | 30 | } else { |
136 | - if (session != null && file.endsWith(".jsp")) { | 31 | + logger.info("Skipping H2 Console Servlet creation"); |
137 | - String page = new String(bytes, Constants.UTF8); | ||
138 | - page = PageParser.parse(page, session.map); | ||
139 | - bytes = page.getBytes(Constants.UTF8); | ||
140 | - } | ||
141 | - resp.setContentType(mimeType); | ||
142 | - if (!cache) { | ||
143 | - resp.setHeader("Cache-Control", "no-cache"); | ||
144 | - } else { | ||
145 | - resp.setHeader("Cache-Control", "max-age=10"); | ||
146 | - resp.setHeader("Last-Modified", server.getStartDateTime()); | ||
147 | - } | ||
148 | } | 32 | } |
149 | - if (bytes != null) { | ||
150 | - ServletOutputStream out = resp.getOutputStream(); | ||
151 | - out.write(bytes); | ||
152 | - } | ||
153 | - } | ||
154 | 33 | ||
155 | - @Override | ||
156 | - public void doPost(HttpServletRequest req, HttpServletResponse resp) | ||
157 | - throws IOException { | ||
158 | - doGet(req, resp); | ||
159 | } | 34 | } |
160 | } | 35 | } |
36 | + | ... | ... |
1 | -package org.legrog.util; | ||
2 | -// Code copied from org.h2.server.web.PageParser | ||
3 | -/* | ||
4 | - * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0, | ||
5 | - * and the EPL 1.0 (http://h2database.com/html/license.html). | ||
6 | - * Initial Developer: H2 Group | ||
7 | - */ | ||
8 | -//package org.h2.server.web; | ||
9 | - | ||
10 | - import java.text.ParseException; | ||
11 | - import java.util.HashMap; | ||
12 | - import java.util.List; | ||
13 | - import java.util.Map; | ||
14 | - import org.h2.util.New; | ||
15 | - | ||
16 | -/** | ||
17 | - * A page parser can parse an HTML page and replace the tags there. | ||
18 | - * This class is used by the H2 Console. | ||
19 | - */ | ||
20 | -public class PageParser { | ||
21 | - private static final int TAB_WIDTH = 4; | ||
22 | - | ||
23 | - private final String page; | ||
24 | - private int pos; | ||
25 | - private final Map<String, Object> settings; | ||
26 | - private final int len; | ||
27 | - private StringBuilder result; | ||
28 | - | ||
29 | - private PageParser(String page, Map<String, Object> settings, int pos) { | ||
30 | - this.page = page; | ||
31 | - this.pos = pos; | ||
32 | - this.len = page.length(); | ||
33 | - this.settings = settings; | ||
34 | - result = new StringBuilder(len); | ||
35 | - } | ||
36 | - | ||
37 | - /** | ||
38 | - * Replace the tags in the HTML page with the given settings. | ||
39 | - * | ||
40 | - * @param page the HTML page | ||
41 | - * @param settings the settings | ||
42 | - * @return the converted page | ||
43 | - */ | ||
44 | - public static String parse(String page, Map<String, Object> settings) { | ||
45 | - PageParser block = new PageParser(page, settings, 0); | ||
46 | - return block.replaceTags(); | ||
47 | - } | ||
48 | - | ||
49 | - private void setError(int i) { | ||
50 | - String s = page.substring(0, i) + "####BUG####" + page.substring(i); | ||
51 | - s = PageParser.escapeHtml(s); | ||
52 | - result = new StringBuilder(); | ||
53 | - result.append(s); | ||
54 | - } | ||
55 | - | ||
56 | - private String parseBlockUntil(String end) throws ParseException { | ||
57 | - PageParser block = new PageParser(page, settings, pos); | ||
58 | - block.parseAll(); | ||
59 | - if (!block.readIf(end)) { | ||
60 | - throw new ParseException(page, block.pos); | ||
61 | - } | ||
62 | - pos = block.pos; | ||
63 | - return block.result.toString(); | ||
64 | - } | ||
65 | - | ||
66 | - private String replaceTags() { | ||
67 | - try { | ||
68 | - parseAll(); | ||
69 | - if (pos != len) { | ||
70 | - setError(pos); | ||
71 | - } | ||
72 | - } catch (ParseException e) { | ||
73 | - setError(pos); | ||
74 | - } | ||
75 | - return result.toString(); | ||
76 | - } | ||
77 | - | ||
78 | - @SuppressWarnings("unchecked") | ||
79 | - private void parseAll() throws ParseException { | ||
80 | - StringBuilder buff = result; | ||
81 | - String p = page; | ||
82 | - int i = pos; | ||
83 | - for (; i < len; i++) { | ||
84 | - char c = p.charAt(i); | ||
85 | - switch (c) { | ||
86 | - case '<': { | ||
87 | - if (p.charAt(i + 3) == ':' && p.charAt(i + 1) == '/') { | ||
88 | - // end tag | ||
89 | - pos = i; | ||
90 | - return; | ||
91 | - } else if (p.charAt(i + 2) == ':') { | ||
92 | - pos = i; | ||
93 | - if (readIf("<c:forEach")) { | ||
94 | - String var = readParam("var"); | ||
95 | - String items = readParam("items"); | ||
96 | - read(">"); | ||
97 | - int start = pos; | ||
98 | - List<Object> list = (List<Object>) get(items); | ||
99 | - if (list == null) { | ||
100 | - result.append("?items?"); | ||
101 | - list = New.arrayList(); | ||
102 | - } | ||
103 | - if (list.size() == 0) { | ||
104 | - parseBlockUntil("</c:forEach>"); | ||
105 | - } | ||
106 | - for (Object o : list) { | ||
107 | - settings.put(var, o); | ||
108 | - pos = start; | ||
109 | - String block = parseBlockUntil("</c:forEach>"); | ||
110 | - result.append(block); | ||
111 | - } | ||
112 | - } else if (readIf("<c:if")) { | ||
113 | - String test = readParam("test"); | ||
114 | - int eq = test.indexOf("=='"); | ||
115 | - if (eq < 0) { | ||
116 | - setError(i); | ||
117 | - return; | ||
118 | - } | ||
119 | - String val = test.substring(eq + 3, test.length() - 1); | ||
120 | - test = test.substring(0, eq); | ||
121 | - String value = (String) get(test); | ||
122 | - read(">"); | ||
123 | - String block = parseBlockUntil("</c:if>"); | ||
124 | - pos--; | ||
125 | - if (value.equals(val)) { | ||
126 | - result.append(block); | ||
127 | - } | ||
128 | - } else { | ||
129 | - setError(i); | ||
130 | - return; | ||
131 | - } | ||
132 | - i = pos; | ||
133 | - } else { | ||
134 | - buff.append(c); | ||
135 | - } | ||
136 | - break; | ||
137 | - } | ||
138 | - case '$': | ||
139 | - if (p.length() > i + 1 && p.charAt(i + 1) == '{') { | ||
140 | - i += 2; | ||
141 | - int j = p.indexOf('}', i); | ||
142 | - if (j < 0) { | ||
143 | - setError(i); | ||
144 | - return; | ||
145 | - } | ||
146 | - String item = p.substring(i, j).trim(); | ||
147 | - i = j; | ||
148 | - String s = (String) get(item); | ||
149 | - replaceTags(s); | ||
150 | - } else { | ||
151 | - buff.append(c); | ||
152 | - } | ||
153 | - break; | ||
154 | - default: | ||
155 | - buff.append(c); | ||
156 | - break; | ||
157 | - } | ||
158 | - } | ||
159 | - pos = i; | ||
160 | - } | ||
161 | - | ||
162 | - @SuppressWarnings("unchecked") | ||
163 | - private Object get(String item) { | ||
164 | - int dot = item.indexOf('.'); | ||
165 | - if (dot >= 0) { | ||
166 | - String sub = item.substring(dot + 1); | ||
167 | - item = item.substring(0, dot); | ||
168 | - HashMap<String, Object> map = (HashMap<String, Object>) settings.get(item); | ||
169 | - if (map == null) { | ||
170 | - return "?" + item + "?"; | ||
171 | - } | ||
172 | - return map.get(sub); | ||
173 | - } | ||
174 | - return settings.get(item); | ||
175 | - } | ||
176 | - | ||
177 | - private void replaceTags(String s) { | ||
178 | - if (s != null) { | ||
179 | - result.append(PageParser.parse(s, settings)); | ||
180 | - } | ||
181 | - } | ||
182 | - | ||
183 | - private String readParam(String name) throws ParseException { | ||
184 | - read(name); | ||
185 | - read("="); | ||
186 | - read("\""); | ||
187 | - int start = pos; | ||
188 | - while (page.charAt(pos) != '"') { | ||
189 | - pos++; | ||
190 | - } | ||
191 | - int end = pos; | ||
192 | - read("\""); | ||
193 | - String s = page.substring(start, end); | ||
194 | - return PageParser.parse(s, settings); | ||
195 | - } | ||
196 | - | ||
197 | - private void skipSpaces() { | ||
198 | - while (page.charAt(pos) == ' ') { | ||
199 | - pos++; | ||
200 | - } | ||
201 | - } | ||
202 | - | ||
203 | - private void read(String s) throws ParseException { | ||
204 | - if (!readIf(s)) { | ||
205 | - throw new ParseException(s, pos); | ||
206 | - } | ||
207 | - } | ||
208 | - | ||
209 | - private boolean readIf(String s) { | ||
210 | - skipSpaces(); | ||
211 | - if (page.regionMatches(pos, s, 0, s.length())) { | ||
212 | - pos += s.length(); | ||
213 | - skipSpaces(); | ||
214 | - return true; | ||
215 | - } | ||
216 | - return false; | ||
217 | - } | ||
218 | - | ||
219 | - /** | ||
220 | - * Convert data to HTML, but don't convert newlines and multiple spaces. | ||
221 | - * | ||
222 | - * @param s the data | ||
223 | - * @return the escaped html text | ||
224 | - */ | ||
225 | - static String escapeHtmlData(String s) { | ||
226 | - return escapeHtml(s, false); | ||
227 | - } | ||
228 | - | ||
229 | - /** | ||
230 | - * Convert data to HTML, including newlines and multiple spaces. | ||
231 | - * | ||
232 | - * @param s the data | ||
233 | - * @return the escaped html text | ||
234 | - */ | ||
235 | - public static String escapeHtml(String s) { | ||
236 | - return escapeHtml(s, true); | ||
237 | - } | ||
238 | - | ||
239 | - private static String escapeHtml(String s, boolean convertBreakAndSpace) { | ||
240 | - if (s == null) { | ||
241 | - return null; | ||
242 | - } | ||
243 | - if (convertBreakAndSpace) { | ||
244 | - if (s.length() == 0) { | ||
245 | - return " "; | ||
246 | - } | ||
247 | - } | ||
248 | - StringBuilder buff = new StringBuilder(s.length()); | ||
249 | - boolean convertSpace = true; | ||
250 | - for (int i = 0; i < s.length(); i++) { | ||
251 | - char c = s.charAt(i); | ||
252 | - if (c == ' ' || c == '\t') { | ||
253 | - // convert tabs into spaces | ||
254 | - for (int j = 0; j < (c == ' ' ? 1 : TAB_WIDTH); j++) { | ||
255 | - if (convertSpace && convertBreakAndSpace) { | ||
256 | - buff.append(" "); | ||
257 | - } else { | ||
258 | - buff.append(' '); | ||
259 | - convertSpace = true; | ||
260 | - } | ||
261 | - } | ||
262 | - continue; | ||
263 | - } | ||
264 | - convertSpace = false; | ||
265 | - switch (c) { | ||
266 | - case '$': | ||
267 | - // so that ${ } in the text is interpreted correctly | ||
268 | - buff.append("$"); | ||
269 | - break; | ||
270 | - case '<': | ||
271 | - buff.append("<"); | ||
272 | - break; | ||
273 | - case '>': | ||
274 | - buff.append(">"); | ||
275 | - break; | ||
276 | - case '&': | ||
277 | - buff.append("&"); | ||
278 | - break; | ||
279 | - case '"': | ||
280 | - buff.append("""); | ||
281 | - break; | ||
282 | - case '\'': | ||
283 | - buff.append("'"); | ||
284 | - break; | ||
285 | - case '\n': | ||
286 | - if (convertBreakAndSpace) { | ||
287 | - buff.append("<br />"); | ||
288 | - convertSpace = true; | ||
289 | - } else { | ||
290 | - buff.append(c); | ||
291 | - } | ||
292 | - break; | ||
293 | - default: | ||
294 | - if (c >= 128) { | ||
295 | - buff.append("&#").append((int) c).append(';'); | ||
296 | - } else { | ||
297 | - buff.append(c); | ||
298 | - } | ||
299 | - break; | ||
300 | - } | ||
301 | - } | ||
302 | - return buff.toString(); | ||
303 | - } | ||
304 | - | ||
305 | - /** | ||
306 | - * Escape text as a the javascript string. | ||
307 | - * | ||
308 | - * @param s the text | ||
309 | - * @return the javascript string | ||
310 | - */ | ||
311 | - static String escapeJavaScript(String s) { | ||
312 | - if (s == null) { | ||
313 | - return null; | ||
314 | - } | ||
315 | - if (s.length() == 0) { | ||
316 | - return ""; | ||
317 | - } | ||
318 | - StringBuilder buff = new StringBuilder(s.length()); | ||
319 | - for (int i = 0; i < s.length(); i++) { | ||
320 | - char c = s.charAt(i); | ||
321 | - switch (c) { | ||
322 | - case '"': | ||
323 | - buff.append("\\\""); | ||
324 | - break; | ||
325 | - case '\'': | ||
326 | - buff.append("\\'"); | ||
327 | - break; | ||
328 | - case '\\': | ||
329 | - buff.append("\\\\"); | ||
330 | - break; | ||
331 | - case '\n': | ||
332 | - buff.append("\\n"); | ||
333 | - break; | ||
334 | - case '\r': | ||
335 | - buff.append("\\r"); | ||
336 | - break; | ||
337 | - case '\t': | ||
338 | - buff.append("\\t"); | ||
339 | - break; | ||
340 | - default: | ||
341 | - buff.append(c); | ||
342 | - break; | ||
343 | - } | ||
344 | - } | ||
345 | - return buff.toString(); | ||
346 | - } | ||
347 | -} | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
1 | -package org.legrog.util; | ||
2 | -// Code copied from org.h2.server.web.WebApp | ||
3 | -/* | ||
4 | - * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0, | ||
5 | - * and the EPL 1.0 (http://h2database.com/html/license.html). | ||
6 | - * Initial Developer: H2 Group | ||
7 | - */ | ||
8 | -//package org.h2.server.web; | ||
9 | - | ||
10 | - import java.io.ByteArrayOutputStream; | ||
11 | - import java.io.PrintStream; | ||
12 | - import java.io.PrintWriter; | ||
13 | - import java.io.StringReader; | ||
14 | - import java.io.StringWriter; | ||
15 | - import java.lang.reflect.InvocationTargetException; | ||
16 | - import java.lang.reflect.Method; | ||
17 | - import java.math.BigDecimal; | ||
18 | - import java.sql.Connection; | ||
19 | - import java.sql.DatabaseMetaData; | ||
20 | - import java.sql.ParameterMetaData; | ||
21 | - import java.sql.PreparedStatement; | ||
22 | - import java.sql.ResultSet; | ||
23 | - import java.sql.ResultSetMetaData; | ||
24 | - import java.sql.SQLException; | ||
25 | - import java.sql.Statement; | ||
26 | - import java.sql.Types; | ||
27 | - import java.util.ArrayList; | ||
28 | - import java.util.Arrays; | ||
29 | - import java.util.Collections; | ||
30 | - import java.util.Comparator; | ||
31 | - import java.util.HashMap; | ||
32 | - import java.util.Iterator; | ||
33 | - import java.util.Locale; | ||
34 | - import java.util.Map; | ||
35 | - import java.util.Properties; | ||
36 | - import java.util.Random; | ||
37 | - import org.h2.api.ErrorCode; | ||
38 | - import org.h2.bnf.Bnf; | ||
39 | - import org.h2.bnf.context.DbColumn; | ||
40 | - import org.h2.bnf.context.DbContents; | ||
41 | - import org.h2.bnf.context.DbSchema; | ||
42 | - import org.h2.bnf.context.DbTableOrView; | ||
43 | - import org.h2.engine.Constants; | ||
44 | - import org.h2.engine.SysProperties; | ||
45 | - import org.h2.jdbc.JdbcSQLException; | ||
46 | - import org.h2.message.DbException; | ||
47 | - import org.h2.security.SHA256; | ||
48 | - import org.h2.tools.Backup; | ||
49 | - import org.h2.tools.ChangeFileEncryption; | ||
50 | - import org.h2.tools.ConvertTraceFile; | ||
51 | - import org.h2.tools.CreateCluster; | ||
52 | - import org.h2.tools.DeleteDbFiles; | ||
53 | - import org.h2.tools.Recover; | ||
54 | - import org.h2.tools.Restore; | ||
55 | - import org.h2.tools.RunScript; | ||
56 | - import org.h2.tools.Script; | ||
57 | - import org.h2.tools.SimpleResultSet; | ||
58 | - import org.h2.util.JdbcUtils; | ||
59 | - import org.h2.util.New; | ||
60 | - import org.h2.util.Profiler; | ||
61 | - import org.h2.util.ScriptReader; | ||
62 | - import org.h2.util.SortedProperties; | ||
63 | - import org.h2.util.StatementBuilder; | ||
64 | - import org.h2.util.StringUtils; | ||
65 | - import org.h2.util.Tool; | ||
66 | - import org.h2.util.Utils; | ||
67 | - | ||
68 | -/** | ||
69 | - * For each connection to a session, an object of this class is created. | ||
70 | - * This class is used by the H2 Console. | ||
71 | - */ | ||
72 | -public class WebApp { | ||
73 | - | ||
74 | - /** | ||
75 | - * The web server. | ||
76 | - */ | ||
77 | - protected final WebServer server; | ||
78 | - | ||
79 | - /** | ||
80 | - * The session. | ||
81 | - */ | ||
82 | - protected WebSession session; | ||
83 | - | ||
84 | - /** | ||
85 | - * The session attributes | ||
86 | - */ | ||
87 | - protected Properties attributes; | ||
88 | - | ||
89 | - /** | ||
90 | - * The mime type of the current response. | ||
91 | - */ | ||
92 | - protected String mimeType; | ||
93 | - | ||
94 | - /** | ||
95 | - * Whether the response can be cached. | ||
96 | - */ | ||
97 | - protected boolean cache; | ||
98 | - | ||
99 | - /** | ||
100 | - * Whether to close the connection. | ||
101 | - */ | ||
102 | - protected boolean stop; | ||
103 | - | ||
104 | - /** | ||
105 | - * The language in the HTTP header. | ||
106 | - */ | ||
107 | - protected String headerLanguage; | ||
108 | - | ||
109 | - private Profiler profiler; | ||
110 | - | ||
111 | - WebApp(WebServer server) { | ||
112 | - this.server = server; | ||
113 | - } | ||
114 | - | ||
115 | - /** | ||
116 | - * Set the web session and attributes. | ||
117 | - * | ||
118 | - * @param session the session | ||
119 | - * @param attributes the attributes | ||
120 | - */ | ||
121 | - void setSession(WebSession session, Properties attributes) { | ||
122 | - this.session = session; | ||
123 | - this.attributes = attributes; | ||
124 | - } | ||
125 | - | ||
126 | - /** | ||
127 | - * Process an HTTP request. | ||
128 | - * | ||
129 | - * @param file the file that was requested | ||
130 | - * @param hostAddr the host address | ||
131 | - * @return the name of the file to return to the client | ||
132 | - */ | ||
133 | - String processRequest(String file, String hostAddr) { | ||
134 | - int index = file.lastIndexOf('.'); | ||
135 | - String suffix; | ||
136 | - if (index >= 0) { | ||
137 | - suffix = file.substring(index + 1); | ||
138 | - } else { | ||
139 | - suffix = ""; | ||
140 | - } | ||
141 | - if ("ico".equals(suffix)) { | ||
142 | - mimeType = "image/x-icon"; | ||
143 | - cache = true; | ||
144 | - } else if ("gif".equals(suffix)) { | ||
145 | - mimeType = "image/gif"; | ||
146 | - cache = true; | ||
147 | - } else if ("css".equals(suffix)) { | ||
148 | - cache = true; | ||
149 | - mimeType = "text/css"; | ||
150 | - } else if ("html".equals(suffix) || | ||
151 | - "do".equals(suffix) || | ||
152 | - "jsp".equals(suffix)) { | ||
153 | - cache = false; | ||
154 | - mimeType = "text/html"; | ||
155 | - if (session == null) { | ||
156 | - session = server.createNewSession(hostAddr); | ||
157 | - if (!"notAllowed.jsp".equals(file)) { | ||
158 | - file = "index.do"; | ||
159 | - } | ||
160 | - } | ||
161 | - } else if ("js".equals(suffix)) { | ||
162 | - cache = true; | ||
163 | - mimeType = "text/javascript"; | ||
164 | - } else { | ||
165 | - cache = true; | ||
166 | - mimeType = "application/octet-stream"; | ||
167 | - } | ||
168 | - trace("mimeType=" + mimeType); | ||
169 | - trace(file); | ||
170 | - if (file.endsWith(".do")) { | ||
171 | - file = process(file); | ||
172 | - } | ||
173 | - return file; | ||
174 | - } | ||
175 | - | ||
176 | - private static String getComboBox(String[] elements, String selected) { | ||
177 | - StringBuilder buff = new StringBuilder(); | ||
178 | - for (String value : elements) { | ||
179 | - buff.append("<option value=\""). | ||
180 | - append(PageParser.escapeHtmlData(value)). | ||
181 | - append('\"'); | ||
182 | - if (value.equals(selected)) { | ||
183 | - buff.append(" selected"); | ||
184 | - } | ||
185 | - buff.append('>'). | ||
186 | - append(PageParser.escapeHtml(value)). | ||
187 | - append("</option>"); | ||
188 | - } | ||
189 | - return buff.toString(); | ||
190 | - } | ||
191 | - | ||
192 | - private static String getComboBox(String[][] elements, String selected) { | ||
193 | - StringBuilder buff = new StringBuilder(); | ||
194 | - for (String[] n : elements) { | ||
195 | - buff.append("<option value=\""). | ||
196 | - append(PageParser.escapeHtmlData(n[0])). | ||
197 | - append('\"'); | ||
198 | - if (n[0].equals(selected)) { | ||
199 | - buff.append(" selected"); | ||
200 | - } | ||
201 | - buff.append('>'). | ||
202 | - append(PageParser.escapeHtml(n[1])). | ||
203 | - append("</option>"); | ||
204 | - } | ||
205 | - return buff.toString(); | ||
206 | - } | ||
207 | - | ||
208 | - private String process(String file) { | ||
209 | - trace("process " + file); | ||
210 | - while (file.endsWith(".do")) { | ||
211 | - if ("login.do".equals(file)) { | ||
212 | - file = login(); | ||
213 | - } else if ("index.do".equals(file)) { | ||
214 | - file = index(); | ||
215 | - } else if ("logout.do".equals(file)) { | ||
216 | - file = logout(); | ||
217 | - } else if ("settingRemove.do".equals(file)) { | ||
218 | - file = settingRemove(); | ||
219 | - } else if ("settingSave.do".equals(file)) { | ||
220 | - file = settingSave(); | ||
221 | - } else if ("test.do".equals(file)) { | ||
222 | - file = test(); | ||
223 | - } else if ("query.do".equals(file)) { | ||
224 | - file = query(); | ||
225 | - } else if ("tables.do".equals(file)) { | ||
226 | - file = tables(); | ||
227 | - } else if ("editResult.do".equals(file)) { | ||
228 | - file = editResult(); | ||
229 | - } else if ("getHistory.do".equals(file)) { | ||
230 | - file = getHistory(); | ||
231 | - } else if ("admin.do".equals(file)) { | ||
232 | - file = admin(); | ||
233 | - } else if ("adminSave.do".equals(file)) { | ||
234 | - file = adminSave(); | ||
235 | - } else if ("adminStartTranslate.do".equals(file)) { | ||
236 | - file = adminStartTranslate(); | ||
237 | - } else if ("adminShutdown.do".equals(file)) { | ||
238 | - file = adminShutdown(); | ||
239 | - } else if ("autoCompleteList.do".equals(file)) { | ||
240 | - file = autoCompleteList(); | ||
241 | - } else if ("tools.do".equals(file)) { | ||
242 | - file = tools(); | ||
243 | - } else { | ||
244 | - file = "error.jsp"; | ||
245 | - } | ||
246 | - } | ||
247 | - trace("return " + file); | ||
248 | - return file; | ||
249 | - } | ||
250 | - | ||
251 | - private String autoCompleteList() { | ||
252 | - String query = (String) attributes.get("query"); | ||
253 | - boolean lowercase = false; | ||
254 | - if (query.trim().length() > 0 && | ||
255 | - Character.isLowerCase(query.trim().charAt(0))) { | ||
256 | - lowercase = true; | ||
257 | - } | ||
258 | - try { | ||
259 | - String sql = query; | ||
260 | - if (sql.endsWith(";")) { | ||
261 | - sql += " "; | ||
262 | - } | ||
263 | - ScriptReader reader = new ScriptReader(new StringReader(sql)); | ||
264 | - reader.setSkipRemarks(true); | ||
265 | - String lastSql = ""; | ||
266 | - while (true) { | ||
267 | - String n = reader.readStatement(); | ||
268 | - if (n == null) { | ||
269 | - break; | ||
270 | - } | ||
271 | - lastSql = n; | ||
272 | - } | ||
273 | - String result = ""; | ||
274 | - if (reader.isInsideRemark()) { | ||
275 | - if (reader.isBlockRemark()) { | ||
276 | - result = "1#(End Remark)# */\n" + result; | ||
277 | - } else { | ||
278 | - result = "1#(Newline)#\n" + result; | ||
279 | - } | ||
280 | - } else { | ||
281 | - sql = lastSql; | ||
282 | - while (sql.length() > 0 && sql.charAt(0) <= ' ') { | ||
283 | - sql = sql.substring(1); | ||
284 | - } | ||
285 | - if (sql.trim().length() > 0 && Character.isLowerCase(sql.trim().charAt(0))) { | ||
286 | - lowercase = true; | ||
287 | - } | ||
288 | - Bnf bnf = session.getBnf(); | ||
289 | - if (bnf == null) { | ||
290 | - return "autoCompleteList.jsp"; | ||
291 | - } | ||
292 | - HashMap<String, String> map = bnf.getNextTokenList(sql); | ||
293 | - String space = ""; | ||
294 | - if (sql.length() > 0) { | ||
295 | - char last = sql.charAt(sql.length() - 1); | ||
296 | - if (!Character.isWhitespace(last) && (last != '.' && | ||
297 | - last >= ' ' && last != '\'' && last != '"')) { | ||
298 | - space = " "; | ||
299 | - } | ||
300 | - } | ||
301 | - ArrayList<String> list = New.arrayList(map.size()); | ||
302 | - for (Map.Entry<String, String> entry : map.entrySet()) { | ||
303 | - String key = entry.getKey(); | ||
304 | - String value = entry.getValue(); | ||
305 | - String type = "" + key.charAt(0); | ||
306 | - if (Integer.parseInt(type) > 2) { | ||
307 | - continue; | ||
308 | - } | ||
309 | - key = key.substring(2); | ||
310 | - if (Character.isLetter(key.charAt(0)) && lowercase) { | ||
311 | - key = StringUtils.toLowerEnglish(key); | ||
312 | - value = StringUtils.toLowerEnglish(value); | ||
313 | - } | ||
314 | - if (key.equals(value) && !".".equals(value)) { | ||
315 | - value = space + value; | ||
316 | - } | ||
317 | - key = StringUtils.urlEncode(key); | ||
318 | - key = StringUtils.replaceAll(key, "+", " "); | ||
319 | - value = StringUtils.urlEncode(value); | ||
320 | - value = StringUtils.replaceAll(value, "+", " "); | ||
321 | - list.add(type + "#" + key + "#" + value); | ||
322 | - } | ||
323 | - Collections.sort(list); | ||
324 | - if (query.endsWith("\n") || query.trim().endsWith(";")) { | ||
325 | - list.add(0, "1#(Newline)#\n"); | ||
326 | - } | ||
327 | - StatementBuilder buff = new StatementBuilder(); | ||
328 | - for (String s : list) { | ||
329 | - buff.appendExceptFirst("|"); | ||
330 | - buff.append(s); | ||
331 | - } | ||
332 | - result = buff.toString(); | ||
333 | - } | ||
334 | - session.put("autoCompleteList", result); | ||
335 | - } catch (Throwable e) { | ||
336 | - server.traceError(e); | ||
337 | - } | ||
338 | - return "autoCompleteList.jsp"; | ||
339 | - } | ||
340 | - | ||
341 | - private String admin() { | ||
342 | - session.put("port", "" + server.getPort()); | ||
343 | - session.put("allowOthers", "" + server.getAllowOthers()); | ||
344 | - session.put("ssl", String.valueOf(server.getSSL())); | ||
345 | - session.put("sessions", server.getSessions()); | ||
346 | - return "admin.jsp"; | ||
347 | - } | ||
348 | - | ||
349 | - private String adminSave() { | ||
350 | - try { | ||
351 | - Properties prop = new SortedProperties(); | ||
352 | - int port = Integer.decode((String) attributes.get("port")); | ||
353 | - prop.setProperty("webPort", String.valueOf(port)); | ||
354 | - server.setPort(port); | ||
355 | - boolean allowOthers = Boolean.parseBoolean( | ||
356 | - (String) attributes.get("allowOthers")); | ||
357 | - prop.setProperty("webAllowOthers", String.valueOf(allowOthers)); | ||
358 | - server.setAllowOthers(allowOthers); | ||
359 | - boolean ssl = Boolean.parseBoolean( | ||
360 | - (String) attributes.get("ssl")); | ||
361 | - prop.setProperty("webSSL", String.valueOf(ssl)); | ||
362 | - server.setSSL(ssl); | ||
363 | - server.saveProperties(prop); | ||
364 | - } catch (Exception e) { | ||
365 | - trace(e.toString()); | ||
366 | - } | ||
367 | - return admin(); | ||
368 | - } | ||
369 | - | ||
370 | - private String tools() { | ||
371 | - try { | ||
372 | - String toolName = (String) attributes.get("tool"); | ||
373 | - session.put("tool", toolName); | ||
374 | - String args = (String) attributes.get("args"); | ||
375 | - String[] argList = StringUtils.arraySplit(args, ',', false); | ||
376 | - Tool tool = null; | ||
377 | - if ("Backup".equals(toolName)) { | ||
378 | - tool = new Backup(); | ||
379 | - } else if ("Restore".equals(toolName)) { | ||
380 | - tool = new Restore(); | ||
381 | - } else if ("Recover".equals(toolName)) { | ||
382 | - tool = new Recover(); | ||
383 | - } else if ("DeleteDbFiles".equals(toolName)) { | ||
384 | - tool = new DeleteDbFiles(); | ||
385 | - } else if ("ChangeFileEncryption".equals(toolName)) { | ||
386 | - tool = new ChangeFileEncryption(); | ||
387 | - } else if ("Script".equals(toolName)) { | ||
388 | - tool = new Script(); | ||
389 | - } else if ("RunScript".equals(toolName)) { | ||
390 | - tool = new RunScript(); | ||
391 | - } else if ("ConvertTraceFile".equals(toolName)) { | ||
392 | - tool = new ConvertTraceFile(); | ||
393 | - } else if ("CreateCluster".equals(toolName)) { | ||
394 | - tool = new CreateCluster(); | ||
395 | - } else { | ||
396 | - throw DbException.throwInternalError(toolName); | ||
397 | - } | ||
398 | - ByteArrayOutputStream outBuff = new ByteArrayOutputStream(); | ||
399 | - PrintStream out = new PrintStream(outBuff, false, "UTF-8"); | ||
400 | - tool.setOut(out); | ||
401 | - try { | ||
402 | - tool.runTool(argList); | ||
403 | - out.flush(); | ||
404 | - String o = new String(outBuff.toByteArray(), Constants.UTF8); | ||
405 | - String result = PageParser.escapeHtml(o); | ||
406 | - session.put("toolResult", result); | ||
407 | - } catch (Exception e) { | ||
408 | - session.put("toolResult", getStackTrace(0, e, true)); | ||
409 | - } | ||
410 | - } catch (Exception e) { | ||
411 | - server.traceError(e); | ||
412 | - } | ||
413 | - return "tools.jsp"; | ||
414 | - } | ||
415 | - | ||
416 | - private String adminStartTranslate() { | ||
417 | - Map<?, ?> p = Map.class.cast(session.map.get("text")); | ||
418 | - @SuppressWarnings("unchecked") | ||
419 | - Map<Object, Object> p2 = (Map<Object, Object>) p; | ||
420 | - String file = server.startTranslate(p2); | ||
421 | - session.put("translationFile", file); | ||
422 | - return "helpTranslate.jsp"; | ||
423 | - } | ||
424 | - | ||
425 | - /** | ||
426 | - * Stop the application and the server. | ||
427 | - * | ||
428 | - * @return the page to display | ||
429 | - */ | ||
430 | - protected String adminShutdown() { | ||
431 | - server.shutdown(); | ||
432 | - return "admin.jsp"; | ||
433 | - } | ||
434 | - | ||
435 | - private String index() { | ||
436 | - String[][] languageArray = WebServer.LANGUAGES; | ||
437 | - String language = (String) attributes.get("language"); | ||
438 | - Locale locale = session.locale; | ||
439 | - if (language != null) { | ||
440 | - if (locale == null || !StringUtils.toLowerEnglish( | ||
441 | - locale.getLanguage()).equals(language)) { | ||
442 | - locale = new Locale(language, ""); | ||
443 | - server.readTranslations(session, locale.getLanguage()); | ||
444 | - session.put("language", language); | ||
445 | - session.locale = locale; | ||
446 | - } | ||
447 | - } else { | ||
448 | - language = (String) session.get("language"); | ||
449 | - } | ||
450 | - if (language == null) { | ||
451 | - // if the language is not yet known | ||
452 | - // use the last header | ||
453 | - language = headerLanguage; | ||
454 | - } | ||
455 | - session.put("languageCombo", getComboBox(languageArray, language)); | ||
456 | - String[] settingNames = server.getSettingNames(); | ||
457 | - String setting = attributes.getProperty("setting"); | ||
458 | - if (setting == null && settingNames.length > 0) { | ||
459 | - setting = settingNames[0]; | ||
460 | - } | ||
461 | - String combobox = getComboBox(settingNames, setting); | ||
462 | - session.put("settingsList", combobox); | ||
463 | - ConnectionInfo info = server.getSetting(setting); | ||
464 | - if (info == null) { | ||
465 | - info = new ConnectionInfo(); | ||
466 | - } | ||
467 | - session.put("setting", PageParser.escapeHtmlData(setting)); | ||
468 | - session.put("name", PageParser.escapeHtmlData(setting)); | ||
469 | - session.put("driver", PageParser.escapeHtmlData(info.driver)); | ||
470 | - session.put("url", PageParser.escapeHtmlData(info.url)); | ||
471 | - session.put("user", PageParser.escapeHtmlData(info.user)); | ||
472 | - return "index.jsp"; | ||
473 | - } | ||
474 | - | ||
475 | - private String getHistory() { | ||
476 | - int id = Integer.parseInt(attributes.getProperty("id")); | ||
477 | - String sql = session.getCommand(id); | ||
478 | - session.put("query", PageParser.escapeHtmlData(sql)); | ||
479 | - return "query.jsp"; | ||
480 | - } | ||
481 | - | ||
482 | - private static int addColumns(boolean mainSchema, DbTableOrView table, | ||
483 | - StringBuilder buff, int treeIndex, boolean showColumnTypes, | ||
484 | - StringBuilder columnsBuffer) { | ||
485 | - DbColumn[] columns = table.getColumns(); | ||
486 | - for (int i = 0; columns != null && i < columns.length; i++) { | ||
487 | - DbColumn column = columns[i]; | ||
488 | - if (columnsBuffer.length() > 0) { | ||
489 | - columnsBuffer.append(' '); | ||
490 | - } | ||
491 | - columnsBuffer.append(column.getName()); | ||
492 | - String col = escapeIdentifier(column.getName()); | ||
493 | - String level = mainSchema ? ", 1, 1" : ", 2, 2"; | ||
494 | - buff.append("setNode(" + treeIndex + level + ", 'column', '" + | ||
495 | - PageParser.escapeJavaScript(column.getName()) + | ||
496 | - "', 'javascript:ins(\\'" + col + "\\')');\n"); | ||
497 | - treeIndex++; | ||
498 | - if (mainSchema && showColumnTypes) { | ||
499 | - buff.append("setNode(" + treeIndex + ", 2, 2, 'type', '" + | ||
500 | - PageParser.escapeJavaScript(column.getDataType()) + | ||
501 | - "', null);\n"); | ||
502 | - treeIndex++; | ||
503 | - } | ||
504 | - } | ||
505 | - return treeIndex; | ||
506 | - } | ||
507 | - | ||
508 | - private static String escapeIdentifier(String name) { | ||
509 | - return StringUtils.urlEncode( | ||
510 | - PageParser.escapeJavaScript(name)).replace('+', ' '); | ||
511 | - } | ||
512 | - | ||
513 | - /** | ||
514 | - * This class represents index information for the GUI. | ||
515 | - */ | ||
516 | - static class IndexInfo { | ||
517 | - | ||
518 | - /** | ||
519 | - * The index name. | ||
520 | - */ | ||
521 | - String name; | ||
522 | - | ||
523 | - /** | ||
524 | - * The index type name. | ||
525 | - */ | ||
526 | - String type; | ||
527 | - | ||
528 | - /** | ||
529 | - * The indexed columns. | ||
530 | - */ | ||
531 | - String columns; | ||
532 | - } | ||
533 | - | ||
534 | - private static int addIndexes(boolean mainSchema, DatabaseMetaData meta, | ||
535 | - String table, String schema, StringBuilder buff, int treeIndex) | ||
536 | - throws SQLException { | ||
537 | - ResultSet rs; | ||
538 | - try { | ||
539 | - rs = meta.getIndexInfo(null, schema, table, false, true); | ||
540 | - } catch (SQLException e) { | ||
541 | - // SQLite | ||
542 | - return treeIndex; | ||
543 | - } | ||
544 | - HashMap<String, IndexInfo> indexMap = New.hashMap(); | ||
545 | - while (rs.next()) { | ||
546 | - String name = rs.getString("INDEX_NAME"); | ||
547 | - IndexInfo info = indexMap.get(name); | ||
548 | - if (info == null) { | ||
549 | - int t = rs.getInt("TYPE"); | ||
550 | - String type; | ||
551 | - if (t == DatabaseMetaData.tableIndexClustered) { | ||
552 | - type = ""; | ||
553 | - } else if (t == DatabaseMetaData.tableIndexHashed) { | ||
554 | - type = " (${text.tree.hashed})"; | ||
555 | - } else if (t == DatabaseMetaData.tableIndexOther) { | ||
556 | - type = ""; | ||
557 | - } else { | ||
558 | - type = null; | ||
559 | - } | ||
560 | - if (name != null && type != null) { | ||
561 | - info = new IndexInfo(); | ||
562 | - info.name = name; | ||
563 | - type = (rs.getBoolean("NON_UNIQUE") ? | ||
564 | - "${text.tree.nonUnique}" : "${text.tree.unique}") + type; | ||
565 | - info.type = type; | ||
566 | - info.columns = rs.getString("COLUMN_NAME"); | ||
567 | - indexMap.put(name, info); | ||
568 | - } | ||
569 | - } else { | ||
570 | - info.columns += ", " + rs.getString("COLUMN_NAME"); | ||
571 | - } | ||
572 | - } | ||
573 | - rs.close(); | ||
574 | - if (indexMap.size() > 0) { | ||
575 | - String level = mainSchema ? ", 1, 1" : ", 2, 1"; | ||
576 | - String levelIndex = mainSchema ? ", 2, 1" : ", 3, 1"; | ||
577 | - String levelColumnType = mainSchema ? ", 3, 2" : ", 4, 2"; | ||
578 | - buff.append("setNode(" + treeIndex + level + | ||
579 | - ", 'index_az', '${text.tree.indexes}', null);\n"); | ||
580 | - treeIndex++; | ||
581 | - for (IndexInfo info : indexMap.values()) { | ||
582 | - buff.append("setNode(" + treeIndex + levelIndex + | ||
583 | - ", 'index', '" + | ||
584 | - PageParser.escapeJavaScript(info.name) + "', null);\n"); | ||
585 | - treeIndex++; | ||
586 | - buff.append("setNode(" + treeIndex + levelColumnType + | ||
587 | - ", 'type', '" + info.type + "', null);\n"); | ||
588 | - treeIndex++; | ||
589 | - buff.append("setNode(" + treeIndex + levelColumnType + | ||
590 | - ", 'type', '" + | ||
591 | - PageParser.escapeJavaScript(info.columns) + | ||
592 | - "', null);\n"); | ||
593 | - treeIndex++; | ||
594 | - } | ||
595 | - } | ||
596 | - return treeIndex; | ||
597 | - } | ||
598 | - | ||
599 | - private int addTablesAndViews(DbSchema schema, boolean mainSchema, | ||
600 | - StringBuilder buff, int treeIndex) throws SQLException { | ||
601 | - if (schema == null) { | ||
602 | - return treeIndex; | ||
603 | - } | ||
604 | - Connection conn = session.getConnection(); | ||
605 | - DatabaseMetaData meta = session.getMetaData(); | ||
606 | - int level = mainSchema ? 0 : 1; | ||
607 | - boolean showColumns = mainSchema || !schema.isSystem; | ||
608 | - String indentation = ", " + level + ", " + (showColumns ? "1" : "2") + ", "; | ||
609 | - String indentNode = ", " + (level + 1) + ", 2, "; | ||
610 | - DbTableOrView[] tables = schema.getTables(); | ||
611 | - if (tables == null) { | ||
612 | - return treeIndex; | ||
613 | - } | ||
614 | - boolean isOracle = schema.getContents().isOracle(); | ||
615 | - boolean notManyTables = tables.length < SysProperties.CONSOLE_MAX_TABLES_LIST_INDEXES; | ||
616 | - for (DbTableOrView table : tables) { | ||
617 | - if (table.isView()) { | ||
618 | - continue; | ||
619 | - } | ||
620 | - int tableId = treeIndex; | ||
621 | - String tab = table.getQuotedName(); | ||
622 | - if (!mainSchema) { | ||
623 | - tab = schema.quotedName + "." + tab; | ||
624 | - } | ||
625 | - tab = escapeIdentifier(tab); | ||
626 | - buff.append("setNode(" + treeIndex + indentation + " 'table', '" + | ||
627 | - PageParser.escapeJavaScript(table.getName()) + | ||
628 | - "', 'javascript:ins(\\'" + tab + "\\',true)');\n"); | ||
629 | - treeIndex++; | ||
630 | - if (mainSchema || showColumns) { | ||
631 | - StringBuilder columnsBuffer = new StringBuilder(); | ||
632 | - treeIndex = addColumns(mainSchema, table, buff, treeIndex, | ||
633 | - notManyTables, columnsBuffer); | ||
634 | - if (!isOracle && notManyTables) { | ||
635 | - treeIndex = addIndexes(mainSchema, meta, table.getName(), | ||
636 | - schema.name, buff, treeIndex); | ||
637 | - } | ||
638 | - buff.append("addTable('" + | ||
639 | - PageParser.escapeJavaScript(table.getName()) + "', '" + | ||
640 | - PageParser.escapeJavaScript(columnsBuffer.toString()) + | ||
641 | - "', " + tableId + ");\n"); | ||
642 | - } | ||
643 | - } | ||
644 | - tables = schema.getTables(); | ||
645 | - for (DbTableOrView view : tables) { | ||
646 | - if (!view.isView()) { | ||
647 | - continue; | ||
648 | - } | ||
649 | - int tableId = treeIndex; | ||
650 | - String tab = view.getQuotedName(); | ||
651 | - if (!mainSchema) { | ||
652 | - tab = view.getSchema().quotedName + "." + tab; | ||
653 | - } | ||
654 | - tab = escapeIdentifier(tab); | ||
655 | - buff.append("setNode(" + treeIndex + indentation + " 'view', '" + | ||
656 | - PageParser.escapeJavaScript(view.getName()) + | ||
657 | - "', 'javascript:ins(\\'" + tab + "\\',true)');\n"); | ||
658 | - treeIndex++; | ||
659 | - if (mainSchema) { | ||
660 | - StringBuilder columnsBuffer = new StringBuilder(); | ||
661 | - treeIndex = addColumns(mainSchema, view, buff, | ||
662 | - treeIndex, notManyTables, columnsBuffer); | ||
663 | - if (schema.getContents().isH2()) { | ||
664 | - | ||
665 | - try (PreparedStatement prep = conn.prepareStatement("SELECT * FROM " + | ||
666 | - "INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME=?")) { | ||
667 | - prep.setString(1, view.getName()); | ||
668 | - ResultSet rs = prep.executeQuery(); | ||
669 | - if (rs.next()) { | ||
670 | - String sql = rs.getString("SQL"); | ||
671 | - buff.append("setNode(" + treeIndex + indentNode + | ||
672 | - " 'type', '" + | ||
673 | - PageParser.escapeJavaScript(sql) + | ||
674 | - "', null);\n"); | ||
675 | - treeIndex++; | ||
676 | - } | ||
677 | - rs.close(); | ||
678 | - } | ||
679 | - } | ||
680 | - buff.append("addTable('" + | ||
681 | - PageParser.escapeJavaScript(view.getName()) + "', '" + | ||
682 | - PageParser.escapeJavaScript(columnsBuffer.toString()) + | ||
683 | - "', " + tableId + ");\n"); | ||
684 | - } | ||
685 | - } | ||
686 | - return treeIndex; | ||
687 | - } | ||
688 | - | ||
689 | - private String tables() { | ||
690 | - DbContents contents = session.getContents(); | ||
691 | - boolean isH2 = false; | ||
692 | - try { | ||
693 | - String url = (String) session.get("url"); | ||
694 | - Connection conn = session.getConnection(); | ||
695 | - contents.readContents(url, conn); | ||
696 | - session.loadBnf(); | ||
697 | - isH2 = contents.isH2(); | ||
698 | - | ||
699 | - StringBuilder buff = new StringBuilder(); | ||
700 | - buff.append("setNode(0, 0, 0, 'database', '" + PageParser.escapeJavaScript(url) | ||
701 | - + "', null);\n"); | ||
702 | - int treeIndex = 1; | ||
703 | - | ||
704 | - DbSchema defaultSchema = contents.getDefaultSchema(); | ||
705 | - treeIndex = addTablesAndViews(defaultSchema, true, buff, treeIndex); | ||
706 | - DbSchema[] schemas = contents.getSchemas(); | ||
707 | - for (DbSchema schema : schemas) { | ||
708 | - if (schema == defaultSchema || schema == null) { | ||
709 | - continue; | ||
710 | - } | ||
711 | - buff.append("setNode(" + treeIndex + ", 0, 1, 'folder', '" + | ||
712 | - PageParser.escapeJavaScript(schema.name) + | ||
713 | - "', null);\n"); | ||
714 | - treeIndex++; | ||
715 | - treeIndex = addTablesAndViews(schema, false, buff, treeIndex); | ||
716 | - } | ||
717 | - if (isH2) { | ||
718 | - try (Statement stat = conn.createStatement()) { | ||
719 | - ResultSet rs = stat.executeQuery("SELECT * FROM " + | ||
720 | - "INFORMATION_SCHEMA.SEQUENCES ORDER BY SEQUENCE_NAME"); | ||
721 | - for (int i = 0; rs.next(); i++) { | ||
722 | - if (i == 0) { | ||
723 | - buff.append("setNode(" + treeIndex + | ||
724 | - ", 0, 1, 'sequences', '${text.tree.sequences}', null);\n"); | ||
725 | - treeIndex++; | ||
726 | - } | ||
727 | - String name = rs.getString("SEQUENCE_NAME"); | ||
728 | - String current = rs.getString("CURRENT_VALUE"); | ||
729 | - String increment = rs.getString("INCREMENT"); | ||
730 | - buff.append("setNode(" + treeIndex + | ||
731 | - ", 1, 1, 'sequence', '" + | ||
732 | - PageParser.escapeJavaScript(name) + | ||
733 | - "', null);\n"); | ||
734 | - treeIndex++; | ||
735 | - buff.append("setNode(" + treeIndex + | ||
736 | - ", 2, 2, 'type', '${text.tree.current}: " + | ||
737 | - PageParser.escapeJavaScript(current) + | ||
738 | - "', null);\n"); | ||
739 | - treeIndex++; | ||
740 | - if (!"1".equals(increment)) { | ||
741 | - buff.append("setNode(" + | ||
742 | - treeIndex + | ||
743 | - ", 2, 2, 'type', '${text.tree.increment}: " + | ||
744 | - PageParser.escapeJavaScript(increment) + | ||
745 | - "', null);\n"); | ||
746 | - treeIndex++; | ||
747 | - } | ||
748 | - } | ||
749 | - rs.close(); | ||
750 | - rs = stat.executeQuery("SELECT * FROM " + | ||
751 | - "INFORMATION_SCHEMA.USERS ORDER BY NAME"); | ||
752 | - for (int i = 0; rs.next(); i++) { | ||
753 | - if (i == 0) { | ||
754 | - buff.append("setNode(" + treeIndex + | ||
755 | - ", 0, 1, 'users', '${text.tree.users}', null);\n"); | ||
756 | - treeIndex++; | ||
757 | - } | ||
758 | - String name = rs.getString("NAME"); | ||
759 | - String admin = rs.getString("ADMIN"); | ||
760 | - buff.append("setNode(" + treeIndex + | ||
761 | - ", 1, 1, 'user', '" + | ||
762 | - PageParser.escapeJavaScript(name) + | ||
763 | - "', null);\n"); | ||
764 | - treeIndex++; | ||
765 | - if (admin.equalsIgnoreCase("TRUE")) { | ||
766 | - buff.append("setNode(" + treeIndex + | ||
767 | - ", 2, 2, 'type', '${text.tree.admin}', null);\n"); | ||
768 | - treeIndex++; | ||
769 | - } | ||
770 | - } | ||
771 | - rs.close(); | ||
772 | - } | ||
773 | - } | ||
774 | - DatabaseMetaData meta = session.getMetaData(); | ||
775 | - String version = meta.getDatabaseProductName() + " " + | ||
776 | - meta.getDatabaseProductVersion(); | ||
777 | - buff.append("setNode(" + treeIndex + ", 0, 0, 'info', '" + | ||
778 | - PageParser.escapeJavaScript(version) + "', null);\n"); | ||
779 | - buff.append("refreshQueryTables();"); | ||
780 | - session.put("tree", buff.toString()); | ||
781 | - } catch (Exception e) { | ||
782 | - session.put("tree", ""); | ||
783 | - session.put("error", getStackTrace(0, e, isH2)); | ||
784 | - } | ||
785 | - return "tables.jsp"; | ||
786 | - } | ||
787 | - | ||
788 | - private String getStackTrace(int id, Throwable e, boolean isH2) { | ||
789 | - try { | ||
790 | - StringWriter writer = new StringWriter(); | ||
791 | - e.printStackTrace(new PrintWriter(writer)); | ||
792 | - String stackTrace = writer.toString(); | ||
793 | - stackTrace = PageParser.escapeHtml(stackTrace); | ||
794 | - if (isH2) { | ||
795 | - stackTrace = linkToSource(stackTrace); | ||
796 | - } | ||
797 | - stackTrace = StringUtils.replaceAll(stackTrace, "\t", | ||
798 | - " "); | ||
799 | - String message = PageParser.escapeHtml(e.getMessage()); | ||
800 | - String error = "<a class=\"error\" href=\"#\" " + | ||
801 | - "onclick=\"var x=document.getElementById('st" + id + | ||
802 | - "').style;x.display=x.display==''?'none':'';\">" + message + | ||
803 | - "</a>"; | ||
804 | - if (e instanceof SQLException) { | ||
805 | - SQLException se = (SQLException) e; | ||
806 | - error += " " + se.getSQLState() + "/" + se.getErrorCode(); | ||
807 | - if (isH2) { | ||
808 | - int code = se.getErrorCode(); | ||
809 | - error += " <a href=\"http://h2database.com/javadoc/" + | ||
810 | - "org/h2/api/ErrorCode.html#c" + code + | ||
811 | - "\">(${text.a.help})</a>"; | ||
812 | - } | ||
813 | - } | ||
814 | - error += "<span style=\"display: none;\" id=\"st" + id + | ||
815 | - "\"><br />" + stackTrace + "</span>"; | ||
816 | - error = formatAsError(error); | ||
817 | - return error; | ||
818 | - } catch (OutOfMemoryError e2) { | ||
819 | - server.traceError(e); | ||
820 | - return e.toString(); | ||
821 | - } | ||
822 | - } | ||
823 | - | ||
824 | - private static String linkToSource(String s) { | ||
825 | - try { | ||
826 | - StringBuilder result = new StringBuilder(s.length()); | ||
827 | - int idx = s.indexOf("<br />"); | ||
828 | - result.append(s.substring(0, idx)); | ||
829 | - while (true) { | ||
830 | - int start = s.indexOf("org.h2.", idx); | ||
831 | - if (start < 0) { | ||
832 | - result.append(s.substring(idx)); | ||
833 | - break; | ||
834 | - } | ||
835 | - result.append(s.substring(idx, start)); | ||
836 | - int end = s.indexOf(')', start); | ||
837 | - if (end < 0) { | ||
838 | - result.append(s.substring(idx)); | ||
839 | - break; | ||
840 | - } | ||
841 | - String element = s.substring(start, end); | ||
842 | - int open = element.lastIndexOf('('); | ||
843 | - int dotMethod = element.lastIndexOf('.', open - 1); | ||
844 | - int dotClass = element.lastIndexOf('.', dotMethod - 1); | ||
845 | - String packageName = element.substring(0, dotClass); | ||
846 | - int colon = element.lastIndexOf(':'); | ||
847 | - String file = element.substring(open + 1, colon); | ||
848 | - String lineNumber = element.substring(colon + 1, element.length()); | ||
849 | - String fullFileName = packageName.replace('.', '/') + "/" + file; | ||
850 | - result.append("<a href=\"http://h2database.com/html/source.html?file="); | ||
851 | - result.append(fullFileName); | ||
852 | - result.append("&line="); | ||
853 | - result.append(lineNumber); | ||
854 | - result.append("&build="); | ||
855 | - result.append(Constants.BUILD_ID); | ||
856 | - result.append("\">"); | ||
857 | - result.append(element); | ||
858 | - result.append("</a>"); | ||
859 | - idx = end; | ||
860 | - } | ||
861 | - return result.toString(); | ||
862 | - } catch (Throwable t) { | ||
863 | - return s; | ||
864 | - } | ||
865 | - } | ||
866 | - | ||
867 | - private static String formatAsError(String s) { | ||
868 | - return "<div class=\"error\">" + s + "</div>"; | ||
869 | - } | ||
870 | - | ||
871 | - private String test() { | ||
872 | - String driver = attributes.getProperty("driver", ""); | ||
873 | - String url = attributes.getProperty("url", ""); | ||
874 | - String user = attributes.getProperty("user", ""); | ||
875 | - String password = attributes.getProperty("password", ""); | ||
876 | - session.put("driver", driver); | ||
877 | - session.put("url", url); | ||
878 | - session.put("user", user); | ||
879 | - boolean isH2 = url.startsWith("jdbc:h2:"); | ||
880 | - try { | ||
881 | - long start = System.currentTimeMillis(); | ||
882 | - String profOpen = "", profClose = ""; | ||
883 | - Profiler prof = new Profiler(); | ||
884 | - prof.startCollecting(); | ||
885 | - Connection conn; | ||
886 | - try { | ||
887 | - conn = server.getConnection(driver, url, user, password); | ||
888 | - } finally { | ||
889 | - prof.stopCollecting(); | ||
890 | - profOpen = prof.getTop(3); | ||
891 | - } | ||
892 | - prof = new Profiler(); | ||
893 | - prof.startCollecting(); | ||
894 | - try { | ||
895 | - JdbcUtils.closeSilently(conn); | ||
896 | - } finally { | ||
897 | - prof.stopCollecting(); | ||
898 | - profClose = prof.getTop(3); | ||
899 | - } | ||
900 | - long time = System.currentTimeMillis() - start; | ||
901 | - String success; | ||
902 | - if (time > 1000) { | ||
903 | - success = "<a class=\"error\" href=\"#\" " + | ||
904 | - "onclick=\"var x=document.getElementById('prof').style;x." + | ||
905 | - "display=x.display==''?'none':'';\">" + | ||
906 | - "${text.login.testSuccessful}</a>" + | ||
907 | - "<span style=\"display: none;\" id=\"prof\"><br />" + | ||
908 | - PageParser.escapeHtml(profOpen) + | ||
909 | - "<br />" + | ||
910 | - PageParser.escapeHtml(profClose) + | ||
911 | - "</span>"; | ||
912 | - } else { | ||
913 | - success = "${text.login.testSuccessful}"; | ||
914 | - } | ||
915 | - session.put("error", success); | ||
916 | - // session.put("error", "${text.login.testSuccessful}"); | ||
917 | - return "login.jsp"; | ||
918 | - } catch (Exception e) { | ||
919 | - session.put("error", getLoginError(e, isH2)); | ||
920 | - return "login.jsp"; | ||
921 | - } | ||
922 | - } | ||
923 | - | ||
924 | - /** | ||
925 | - * Get the formatted login error message. | ||
926 | - * | ||
927 | - * @param e the exception | ||
928 | - * @param isH2 if the current database is a H2 database | ||
929 | - * @return the formatted error message | ||
930 | - */ | ||
931 | - private String getLoginError(Exception e, boolean isH2) { | ||
932 | - if (e instanceof JdbcSQLException && | ||
933 | - ((JdbcSQLException) e).getErrorCode() == ErrorCode.CLASS_NOT_FOUND_1) { | ||
934 | - return "${text.login.driverNotFound}<br />" + getStackTrace(0, e, isH2); | ||
935 | - } | ||
936 | - return getStackTrace(0, e, isH2); | ||
937 | - } | ||
938 | - | ||
939 | - private String login() { | ||
940 | - String driver = attributes.getProperty("driver", ""); | ||
941 | - String url = attributes.getProperty("url", ""); | ||
942 | - String user = attributes.getProperty("user", ""); | ||
943 | - String password = attributes.getProperty("password", ""); | ||
944 | - session.put("autoCommit", "checked"); | ||
945 | - session.put("autoComplete", "1"); | ||
946 | - session.put("maxrows", "1000"); | ||
947 | - boolean isH2 = url.startsWith("jdbc:h2:"); | ||
948 | - try { | ||
949 | - Connection conn = server.getConnection(driver, url, user, password); | ||
950 | - session.setConnection(conn); | ||
951 | - session.put("url", url); | ||
952 | - session.put("user", user); | ||
953 | - session.remove("error"); | ||
954 | - settingSave(); | ||
955 | - return "frame.jsp"; | ||
956 | - } catch (Exception e) { | ||
957 | - session.put("error", getLoginError(e, isH2)); | ||
958 | - return "login.jsp"; | ||
959 | - } | ||
960 | - } | ||
961 | - | ||
962 | - private String logout() { | ||
963 | - try { | ||
964 | - Connection conn = session.getConnection(); | ||
965 | - session.setConnection(null); | ||
966 | - session.remove("conn"); | ||
967 | - session.remove("result"); | ||
968 | - session.remove("tables"); | ||
969 | - session.remove("user"); | ||
970 | - session.remove("tool"); | ||
971 | - if (conn != null) { | ||
972 | - if (session.getShutdownServerOnDisconnect()) { | ||
973 | - server.shutdown(); | ||
974 | - } else { | ||
975 | - conn.close(); | ||
976 | - } | ||
977 | - } | ||
978 | - } catch (Exception e) { | ||
979 | - trace(e.toString()); | ||
980 | - } | ||
981 | - return "index.do"; | ||
982 | - } | ||
983 | - | ||
984 | - private String query() { | ||
985 | - String sql = attributes.getProperty("sql").trim(); | ||
986 | - try { | ||
987 | - ScriptReader r = new ScriptReader(new StringReader(sql)); | ||
988 | - final ArrayList<String> list = New.arrayList(); | ||
989 | - while (true) { | ||
990 | - String s = r.readStatement(); | ||
991 | - if (s == null) { | ||
992 | - break; | ||
993 | - } | ||
994 | - list.add(s); | ||
995 | - } | ||
996 | - final Connection conn = session.getConnection(); | ||
997 | - if (SysProperties.CONSOLE_STREAM && server.getAllowChunked()) { | ||
998 | - String page = new String(server.getFile("result.jsp"), Constants.UTF8); | ||
999 | - int idx = page.indexOf("${result}"); | ||
1000 | - // the first element of the list is the header, the last the | ||
1001 | - // footer | ||
1002 | - list.add(0, page.substring(0, idx)); | ||
1003 | - list.add(page.substring(idx + "${result}".length())); | ||
1004 | - session.put("chunks", new Iterator<String>() { | ||
1005 | - private int i; | ||
1006 | - @Override | ||
1007 | - public boolean hasNext() { | ||
1008 | - return i < list.size(); | ||
1009 | - } | ||
1010 | - @Override | ||
1011 | - public String next() { | ||
1012 | - String s = list.get(i++); | ||
1013 | - if (i == 1 || i == list.size()) { | ||
1014 | - return s; | ||
1015 | - } | ||
1016 | - StringBuilder b = new StringBuilder(); | ||
1017 | - query(conn, s, i - 1, list.size() - 2, b); | ||
1018 | - return b.toString(); | ||
1019 | - } | ||
1020 | - @Override | ||
1021 | - public void remove() { | ||
1022 | - throw new UnsupportedOperationException(); | ||
1023 | - } | ||
1024 | - }); | ||
1025 | - return "result.jsp"; | ||
1026 | - } | ||
1027 | - String result; | ||
1028 | - StringBuilder buff = new StringBuilder(); | ||
1029 | - for (int i = 0; i < list.size(); i++) { | ||
1030 | - String s = list.get(i); | ||
1031 | - query(conn, s, i, list.size(), buff); | ||
1032 | - } | ||
1033 | - result = buff.toString(); | ||
1034 | - session.put("result", result); | ||
1035 | - } catch (Throwable e) { | ||
1036 | - session.put("result", getStackTrace(0, e, session.getContents().isH2())); | ||
1037 | - } | ||
1038 | - return "result.jsp"; | ||
1039 | - } | ||
1040 | - | ||
1041 | - /** | ||
1042 | - * Execute a query and append the result to the buffer. | ||
1043 | - * | ||
1044 | - * @param conn the connection | ||
1045 | - * @param s the statement | ||
1046 | - * @param i the index | ||
1047 | - * @param size the number of statements | ||
1048 | - * @param buff the target buffer | ||
1049 | - */ | ||
1050 | - void query(Connection conn, String s, int i, int size, StringBuilder buff) { | ||
1051 | - if (!(s.startsWith("@") && s.endsWith("."))) { | ||
1052 | - buff.append(PageParser.escapeHtml(s + ";")).append("<br />"); | ||
1053 | - } | ||
1054 | - boolean forceEdit = s.startsWith("@edit"); | ||
1055 | - buff.append(getResult(conn, i + 1, s, size == 1, forceEdit)). | ||
1056 | - append("<br />"); | ||
1057 | - } | ||
1058 | - | ||
1059 | - private String editResult() { | ||
1060 | - ResultSet rs = session.result; | ||
1061 | - int row = Integer.parseInt(attributes.getProperty("row")); | ||
1062 | - int op = Integer.parseInt(attributes.getProperty("op")); | ||
1063 | - String result = "", error = ""; | ||
1064 | - try { | ||
1065 | - if (op == 1) { | ||
1066 | - boolean insert = row < 0; | ||
1067 | - if (insert) { | ||
1068 | - rs.moveToInsertRow(); | ||
1069 | - } else { | ||
1070 | - rs.absolute(row); | ||
1071 | - } | ||
1072 | - for (int i = 0; i < rs.getMetaData().getColumnCount(); i++) { | ||
1073 | - String x = attributes.getProperty("r" + row + "c" + (i + 1)); | ||
1074 | - unescapeData(x, rs, i + 1); | ||
1075 | - } | ||
1076 | - if (insert) { | ||
1077 | - rs.insertRow(); | ||
1078 | - } else { | ||
1079 | - rs.updateRow(); | ||
1080 | - } | ||
1081 | - } else if (op == 2) { | ||
1082 | - rs.absolute(row); | ||
1083 | - rs.deleteRow(); | ||
1084 | - } else if (op == 3) { | ||
1085 | - // cancel | ||
1086 | - } | ||
1087 | - } catch (Throwable e) { | ||
1088 | - result = "<br />" + getStackTrace(0, e, session.getContents().isH2()); | ||
1089 | - error = formatAsError(e.getMessage()); | ||
1090 | - } | ||
1091 | - String sql = "@edit " + (String) session.get("resultSetSQL"); | ||
1092 | - Connection conn = session.getConnection(); | ||
1093 | - result = error + getResult(conn, -1, sql, true, true) + result; | ||
1094 | - session.put("result", result); | ||
1095 | - return "result.jsp"; | ||
1096 | - } | ||
1097 | - | ||
1098 | - private ResultSet getMetaResultSet(Connection conn, String sql) | ||
1099 | - throws SQLException { | ||
1100 | - DatabaseMetaData meta = conn.getMetaData(); | ||
1101 | - if (isBuiltIn(sql, "@best_row_identifier")) { | ||
1102 | - String[] p = split(sql); | ||
1103 | - int scale = p[4] == null ? 0 : Integer.parseInt(p[4]); | ||
1104 | - boolean nullable = p[5] == null ? false : Boolean.parseBoolean(p[5]); | ||
1105 | - return meta.getBestRowIdentifier(p[1], p[2], p[3], scale, nullable); | ||
1106 | - } else if (isBuiltIn(sql, "@catalogs")) { | ||
1107 | - return meta.getCatalogs(); | ||
1108 | - } else if (isBuiltIn(sql, "@columns")) { | ||
1109 | - String[] p = split(sql); | ||
1110 | - return meta.getColumns(p[1], p[2], p[3], p[4]); | ||
1111 | - } else if (isBuiltIn(sql, "@column_privileges")) { | ||
1112 | - String[] p = split(sql); | ||
1113 | - return meta.getColumnPrivileges(p[1], p[2], p[3], p[4]); | ||
1114 | - } else if (isBuiltIn(sql, "@cross_references")) { | ||
1115 | - String[] p = split(sql); | ||
1116 | - return meta.getCrossReference(p[1], p[2], p[3], p[4], p[5], p[6]); | ||
1117 | - } else if (isBuiltIn(sql, "@exported_keys")) { | ||
1118 | - String[] p = split(sql); | ||
1119 | - return meta.getExportedKeys(p[1], p[2], p[3]); | ||
1120 | - } else if (isBuiltIn(sql, "@imported_keys")) { | ||
1121 | - String[] p = split(sql); | ||
1122 | - return meta.getImportedKeys(p[1], p[2], p[3]); | ||
1123 | - } else if (isBuiltIn(sql, "@index_info")) { | ||
1124 | - String[] p = split(sql); | ||
1125 | - boolean unique = p[4] == null ? false : Boolean.parseBoolean(p[4]); | ||
1126 | - boolean approx = p[5] == null ? false : Boolean.parseBoolean(p[5]); | ||
1127 | - return meta.getIndexInfo(p[1], p[2], p[3], unique, approx); | ||
1128 | - } else if (isBuiltIn(sql, "@primary_keys")) { | ||
1129 | - String[] p = split(sql); | ||
1130 | - return meta.getPrimaryKeys(p[1], p[2], p[3]); | ||
1131 | - } else if (isBuiltIn(sql, "@procedures")) { | ||
1132 | - String[] p = split(sql); | ||
1133 | - return meta.getProcedures(p[1], p[2], p[3]); | ||
1134 | - } else if (isBuiltIn(sql, "@procedure_columns")) { | ||
1135 | - String[] p = split(sql); | ||
1136 | - return meta.getProcedureColumns(p[1], p[2], p[3], p[4]); | ||
1137 | - } else if (isBuiltIn(sql, "@schemas")) { | ||
1138 | - return meta.getSchemas(); | ||
1139 | - } else if (isBuiltIn(sql, "@tables")) { | ||
1140 | - String[] p = split(sql); | ||
1141 | - String[] types = p[4] == null ? null : StringUtils.arraySplit(p[4], ',', false); | ||
1142 | - return meta.getTables(p[1], p[2], p[3], types); | ||
1143 | - } else if (isBuiltIn(sql, "@table_privileges")) { | ||
1144 | - String[] p = split(sql); | ||
1145 | - return meta.getTablePrivileges(p[1], p[2], p[3]); | ||
1146 | - } else if (isBuiltIn(sql, "@table_types")) { | ||
1147 | - return meta.getTableTypes(); | ||
1148 | - } else if (isBuiltIn(sql, "@type_info")) { | ||
1149 | - return meta.getTypeInfo(); | ||
1150 | - } else if (isBuiltIn(sql, "@udts")) { | ||
1151 | - String[] p = split(sql); | ||
1152 | - int[] types; | ||
1153 | - if (p[4] == null) { | ||
1154 | - types = null; | ||
1155 | - } else { | ||
1156 | - String[] t = StringUtils.arraySplit(p[4], ',', false); | ||
1157 | - types = new int[t.length]; | ||
1158 | - for (int i = 0; i < t.length; i++) { | ||
1159 | - types[i] = Integer.parseInt(t[i]); | ||
1160 | - } | ||
1161 | - } | ||
1162 | - return meta.getUDTs(p[1], p[2], p[3], types); | ||
1163 | - } else if (isBuiltIn(sql, "@version_columns")) { | ||
1164 | - String[] p = split(sql); | ||
1165 | - return meta.getVersionColumns(p[1], p[2], p[3]); | ||
1166 | - } else if (isBuiltIn(sql, "@memory")) { | ||
1167 | - SimpleResultSet rs = new SimpleResultSet(); | ||
1168 | - rs.addColumn("Type", Types.VARCHAR, 0, 0); | ||
1169 | - rs.addColumn("KB", Types.VARCHAR, 0, 0); | ||
1170 | - rs.addRow("Used Memory", "" + Utils.getMemoryUsed()); | ||
1171 | - rs.addRow("Free Memory", "" + Utils.getMemoryFree()); | ||
1172 | - return rs; | ||
1173 | - } else if (isBuiltIn(sql, "@info")) { | ||
1174 | - SimpleResultSet rs = new SimpleResultSet(); | ||
1175 | - rs.addColumn("KEY", Types.VARCHAR, 0, 0); | ||
1176 | - rs.addColumn("VALUE", Types.VARCHAR, 0, 0); | ||
1177 | - rs.addRow("conn.getCatalog", conn.getCatalog()); | ||
1178 | - rs.addRow("conn.getAutoCommit", "" + conn.getAutoCommit()); | ||
1179 | - rs.addRow("conn.getTransactionIsolation", "" + conn.getTransactionIsolation()); | ||
1180 | - rs.addRow("conn.getWarnings", "" + conn.getWarnings()); | ||
1181 | - String map; | ||
1182 | - try { | ||
1183 | - map = "" + conn.getTypeMap(); | ||
1184 | - } catch (SQLException e) { | ||
1185 | - map = e.toString(); | ||
1186 | - } | ||
1187 | - rs.addRow("conn.getTypeMap", "" + map); | ||
1188 | - rs.addRow("conn.isReadOnly", "" + conn.isReadOnly()); | ||
1189 | - rs.addRow("conn.getHoldability", "" + conn.getHoldability()); | ||
1190 | - addDatabaseMetaData(rs, meta); | ||
1191 | - return rs; | ||
1192 | - } else if (isBuiltIn(sql, "@attributes")) { | ||
1193 | - String[] p = split(sql); | ||
1194 | - return meta.getAttributes(p[1], p[2], p[3], p[4]); | ||
1195 | - } else if (isBuiltIn(sql, "@super_tables")) { | ||
1196 | - String[] p = split(sql); | ||
1197 | - return meta.getSuperTables(p[1], p[2], p[3]); | ||
1198 | - } else if (isBuiltIn(sql, "@super_types")) { | ||
1199 | - String[] p = split(sql); | ||
1200 | - return meta.getSuperTypes(p[1], p[2], p[3]); | ||
1201 | - } else if (isBuiltIn(sql, "@prof_stop")) { | ||
1202 | - if (profiler != null) { | ||
1203 | - profiler.stopCollecting(); | ||
1204 | - SimpleResultSet rs = new SimpleResultSet(); | ||
1205 | - rs.addColumn("Top Stack Trace(s)", Types.VARCHAR, 0, 0); | ||
1206 | - rs.addRow(profiler.getTop(3)); | ||
1207 | - profiler = null; | ||
1208 | - return rs; | ||
1209 | - } | ||
1210 | - } | ||
1211 | - return null; | ||
1212 | - } | ||
1213 | - | ||
1214 | - private static void addDatabaseMetaData(SimpleResultSet rs, | ||
1215 | - DatabaseMetaData meta) { | ||
1216 | - Method[] methods = DatabaseMetaData.class.getDeclaredMethods(); | ||
1217 | - Arrays.sort(methods, new Comparator<Method>() { | ||
1218 | - @Override | ||
1219 | - public int compare(Method o1, Method o2) { | ||
1220 | - return o1.toString().compareTo(o2.toString()); | ||
1221 | - } | ||
1222 | - }); | ||
1223 | - for (Method m : methods) { | ||
1224 | - if (m.getParameterTypes().length == 0) { | ||
1225 | - try { | ||
1226 | - Object o = m.invoke(meta); | ||
1227 | - rs.addRow("meta." + m.getName(), "" + o); | ||
1228 | - } catch (InvocationTargetException e) { | ||
1229 | - rs.addRow("meta." + m.getName(), e.getTargetException().toString()); | ||
1230 | - } catch (Exception e) { | ||
1231 | - rs.addRow("meta." + m.getName(), e.toString()); | ||
1232 | - } | ||
1233 | - } | ||
1234 | - } | ||
1235 | - } | ||
1236 | - | ||
1237 | - private static String[] split(String s) { | ||
1238 | - String[] list = new String[10]; | ||
1239 | - String[] t = StringUtils.arraySplit(s, ' ', true); | ||
1240 | - System.arraycopy(t, 0, list, 0, t.length); | ||
1241 | - for (int i = 0; i < list.length; i++) { | ||
1242 | - if ("null".equals(list[i])) { | ||
1243 | - list[i] = null; | ||
1244 | - } | ||
1245 | - } | ||
1246 | - return list; | ||
1247 | - } | ||
1248 | - | ||
1249 | - private int getMaxrows() { | ||
1250 | - String r = (String) session.get("maxrows"); | ||
1251 | - int maxrows = r == null ? 0 : Integer.parseInt(r); | ||
1252 | - return maxrows; | ||
1253 | - } | ||
1254 | - | ||
1255 | - private String getResult(Connection conn, int id, String sql, | ||
1256 | - boolean allowEdit, boolean forceEdit) { | ||
1257 | - try { | ||
1258 | - sql = sql.trim(); | ||
1259 | - StringBuilder buff = new StringBuilder(); | ||
1260 | - String sqlUpper = StringUtils.toUpperEnglish(sql); | ||
1261 | - if (sqlUpper.contains("CREATE") || | ||
1262 | - sqlUpper.contains("DROP") || | ||
1263 | - sqlUpper.contains("ALTER") || | ||
1264 | - sqlUpper.contains("RUNSCRIPT")) { | ||
1265 | - String sessionId = attributes.getProperty("jsessionid"); | ||
1266 | - buff.append("<script type=\"text/javascript\">" + | ||
1267 | - "parent['h2menu'].location='tables.do?jsessionid=" | ||
1268 | - + sessionId + "';</script>"); | ||
1269 | - } | ||
1270 | - Statement stat; | ||
1271 | - DbContents contents = session.getContents(); | ||
1272 | - if (forceEdit || (allowEdit && contents.isH2())) { | ||
1273 | - stat = conn.createStatement( | ||
1274 | - ResultSet.TYPE_SCROLL_INSENSITIVE, | ||
1275 | - ResultSet.CONCUR_UPDATABLE); | ||
1276 | - } else { | ||
1277 | - stat = conn.createStatement(); | ||
1278 | - } | ||
1279 | - ResultSet rs; | ||
1280 | - long time = System.currentTimeMillis(); | ||
1281 | - boolean metadata = false; | ||
1282 | - boolean generatedKeys = false; | ||
1283 | - boolean edit = false; | ||
1284 | - boolean list = false; | ||
1285 | - if (isBuiltIn(sql, "@autocommit_true")) { | ||
1286 | - conn.setAutoCommit(true); | ||
1287 | - return "${text.result.autoCommitOn}"; | ||
1288 | - } else if (isBuiltIn(sql, "@autocommit_false")) { | ||
1289 | - conn.setAutoCommit(false); | ||
1290 | - return "${text.result.autoCommitOff}"; | ||
1291 | - } else if (isBuiltIn(sql, "@cancel")) { | ||
1292 | - stat = session.executingStatement; | ||
1293 | - if (stat != null) { | ||
1294 | - stat.cancel(); | ||
1295 | - buff.append("${text.result.statementWasCanceled}"); | ||
1296 | - } else { | ||
1297 | - buff.append("${text.result.noRunningStatement}"); | ||
1298 | - } | ||
1299 | - return buff.toString(); | ||
1300 | - } else if (isBuiltIn(sql, "@edit")) { | ||
1301 | - edit = true; | ||
1302 | - sql = sql.substring("@edit".length()).trim(); | ||
1303 | - session.put("resultSetSQL", sql); | ||
1304 | - } | ||
1305 | - if (isBuiltIn(sql, "@list")) { | ||
1306 | - list = true; | ||
1307 | - sql = sql.substring("@list".length()).trim(); | ||
1308 | - } | ||
1309 | - if (isBuiltIn(sql, "@meta")) { | ||
1310 | - metadata = true; | ||
1311 | - sql = sql.substring("@meta".length()).trim(); | ||
1312 | - } | ||
1313 | - if (isBuiltIn(sql, "@generated")) { | ||
1314 | - generatedKeys = true; | ||
1315 | - sql = sql.substring("@generated".length()).trim(); | ||
1316 | - } else if (isBuiltIn(sql, "@history")) { | ||
1317 | - buff.append(getCommandHistoryString()); | ||
1318 | - return buff.toString(); | ||
1319 | - } else if (isBuiltIn(sql, "@loop")) { | ||
1320 | - sql = sql.substring("@loop".length()).trim(); | ||
1321 | - int idx = sql.indexOf(' '); | ||
1322 | - int count = Integer.decode(sql.substring(0, idx)); | ||
1323 | - sql = sql.substring(idx).trim(); | ||
1324 | - return executeLoop(conn, count, sql); | ||
1325 | - } else if (isBuiltIn(sql, "@maxrows")) { | ||
1326 | - int maxrows = (int) Double.parseDouble( | ||
1327 | - sql.substring("@maxrows".length()).trim()); | ||
1328 | - session.put("maxrows", "" + maxrows); | ||
1329 | - return "${text.result.maxrowsSet}"; | ||
1330 | - } else if (isBuiltIn(sql, "@parameter_meta")) { | ||
1331 | - sql = sql.substring("@parameter_meta".length()).trim(); | ||
1332 | - PreparedStatement prep = conn.prepareStatement(sql); | ||
1333 | - buff.append(getParameterResultSet(prep.getParameterMetaData())); | ||
1334 | - return buff.toString(); | ||
1335 | - } else if (isBuiltIn(sql, "@password_hash")) { | ||
1336 | - sql = sql.substring("@password_hash".length()).trim(); | ||
1337 | - String[] p = split(sql); | ||
1338 | - return StringUtils.convertBytesToHex( | ||
1339 | - SHA256.getKeyPasswordHash(p[0], p[1].toCharArray())); | ||
1340 | - } else if (isBuiltIn(sql, "@prof_start")) { | ||
1341 | - if (profiler != null) { | ||
1342 | - profiler.stopCollecting(); | ||
1343 | - } | ||
1344 | - profiler = new Profiler(); | ||
1345 | - profiler.startCollecting(); | ||
1346 | - return "Ok"; | ||
1347 | - } else if (isBuiltIn(sql, "@sleep")) { | ||
1348 | - String s = sql.substring("@sleep".length()).trim(); | ||
1349 | - int sleep = 1; | ||
1350 | - if (s.length() > 0) { | ||
1351 | - sleep = Integer.parseInt(s); | ||
1352 | - } | ||
1353 | - Thread.sleep(sleep * 1000); | ||
1354 | - return "Ok"; | ||
1355 | - } else if (isBuiltIn(sql, "@transaction_isolation")) { | ||
1356 | - String s = sql.substring("@transaction_isolation".length()).trim(); | ||
1357 | - if (s.length() > 0) { | ||
1358 | - int level = Integer.parseInt(s); | ||
1359 | - conn.setTransactionIsolation(level); | ||
1360 | - } | ||
1361 | - buff.append("Transaction Isolation: " + | ||
1362 | - conn.getTransactionIsolation() + "<br />"); | ||
1363 | - buff.append(Connection.TRANSACTION_READ_UNCOMMITTED + | ||
1364 | - ": read_uncommitted<br />"); | ||
1365 | - buff.append(Connection.TRANSACTION_READ_COMMITTED + | ||
1366 | - ": read_committed<br />"); | ||
1367 | - buff.append(Connection.TRANSACTION_REPEATABLE_READ + | ||
1368 | - ": repeatable_read<br />"); | ||
1369 | - buff.append(Connection.TRANSACTION_SERIALIZABLE + | ||
1370 | - ": serializable"); | ||
1371 | - } | ||
1372 | - if (sql.startsWith("@")) { | ||
1373 | - rs = getMetaResultSet(conn, sql); | ||
1374 | - if (rs == null) { | ||
1375 | - buff.append("?: " + sql); | ||
1376 | - return buff.toString(); | ||
1377 | - } | ||
1378 | - } else { | ||
1379 | - int maxrows = getMaxrows(); | ||
1380 | - stat.setMaxRows(maxrows); | ||
1381 | - session.executingStatement = stat; | ||
1382 | - boolean isResultSet = stat.execute(sql); | ||
1383 | - session.addCommand(sql); | ||
1384 | - if (generatedKeys) { | ||
1385 | - rs = null; | ||
1386 | - rs = stat.getGeneratedKeys(); | ||
1387 | - } else { | ||
1388 | - if (!isResultSet) { | ||
1389 | - buff.append("${text.result.updateCount}: " + stat.getUpdateCount()); | ||
1390 | - time = System.currentTimeMillis() - time; | ||
1391 | - buff.append("<br />(").append(time).append(" ms)"); | ||
1392 | - stat.close(); | ||
1393 | - return buff.toString(); | ||
1394 | - } | ||
1395 | - rs = stat.getResultSet(); | ||
1396 | - } | ||
1397 | - } | ||
1398 | - time = System.currentTimeMillis() - time; | ||
1399 | - buff.append(getResultSet(sql, rs, metadata, list, edit, time, allowEdit)); | ||
1400 | - // SQLWarning warning = stat.getWarnings(); | ||
1401 | - // if (warning != null) { | ||
1402 | - // buff.append("<br />Warning:<br />"). | ||
1403 | - // append(getStackTrace(id, warning)); | ||
1404 | - // } | ||
1405 | - if (!edit) { | ||
1406 | - stat.close(); | ||
1407 | - } | ||
1408 | - return buff.toString(); | ||
1409 | - } catch (Throwable e) { | ||
1410 | - // throwable: including OutOfMemoryError and so on | ||
1411 | - return getStackTrace(id, e, session.getContents().isH2()); | ||
1412 | - } finally { | ||
1413 | - session.executingStatement = null; | ||
1414 | - } | ||
1415 | - } | ||
1416 | - | ||
1417 | - private static boolean isBuiltIn(String sql, String builtIn) { | ||
1418 | - return StringUtils.startsWithIgnoreCase(sql, builtIn); | ||
1419 | - } | ||
1420 | - | ||
1421 | - private String executeLoop(Connection conn, int count, String sql) | ||
1422 | - throws SQLException { | ||
1423 | - ArrayList<Integer> params = New.arrayList(); | ||
1424 | - int idx = 0; | ||
1425 | - while (!stop) { | ||
1426 | - idx = sql.indexOf('?', idx); | ||
1427 | - if (idx < 0) { | ||
1428 | - break; | ||
1429 | - } | ||
1430 | - if (isBuiltIn(sql.substring(idx), "?/*rnd*/")) { | ||
1431 | - params.add(1); | ||
1432 | - sql = sql.substring(0, idx) + "?" + sql.substring(idx + "/*rnd*/".length() + 1); | ||
1433 | - } else { | ||
1434 | - params.add(0); | ||
1435 | - } | ||
1436 | - idx++; | ||
1437 | - } | ||
1438 | - boolean prepared; | ||
1439 | - Random random = new Random(1); | ||
1440 | - long time = System.currentTimeMillis(); | ||
1441 | - if (isBuiltIn(sql, "@statement")) { | ||
1442 | - sql = sql.substring("@statement".length()).trim(); | ||
1443 | - prepared = false; | ||
1444 | - Statement stat = conn.createStatement(); | ||
1445 | - for (int i = 0; !stop && i < count; i++) { | ||
1446 | - String s = sql; | ||
1447 | - for (Integer type : params) { | ||
1448 | - idx = s.indexOf('?'); | ||
1449 | - if (type.intValue() == 1) { | ||
1450 | - s = s.substring(0, idx) + random.nextInt(count) + s.substring(idx + 1); | ||
1451 | - } else { | ||
1452 | - s = s.substring(0, idx) + i + s.substring(idx + 1); | ||
1453 | - } | ||
1454 | - } | ||
1455 | - if (stat.execute(s)) { | ||
1456 | - ResultSet rs = stat.getResultSet(); | ||
1457 | - while (!stop && rs.next()) { | ||
1458 | - // maybe get the data as well | ||
1459 | - } | ||
1460 | - rs.close(); | ||
1461 | - } | ||
1462 | - } | ||
1463 | - } else { | ||
1464 | - prepared = true; | ||
1465 | - PreparedStatement prep = conn.prepareStatement(sql); | ||
1466 | - for (int i = 0; !stop && i < count; i++) { | ||
1467 | - for (int j = 0; j < params.size(); j++) { | ||
1468 | - Integer type = params.get(j); | ||
1469 | - if (type.intValue() == 1) { | ||
1470 | - prep.setInt(j + 1, random.nextInt(count)); | ||
1471 | - } else { | ||
1472 | - prep.setInt(j + 1, i); | ||
1473 | - } | ||
1474 | - } | ||
1475 | - if (session.getContents().isSQLite()) { | ||
1476 | - // SQLite currently throws an exception on prep.execute() | ||
1477 | - prep.executeUpdate(); | ||
1478 | - } else { | ||
1479 | - if (prep.execute()) { | ||
1480 | - ResultSet rs = prep.getResultSet(); | ||
1481 | - while (!stop && rs.next()) { | ||
1482 | - // maybe get the data as well | ||
1483 | - } | ||
1484 | - rs.close(); | ||
1485 | - } | ||
1486 | - } | ||
1487 | - } | ||
1488 | - } | ||
1489 | - time = System.currentTimeMillis() - time; | ||
1490 | - StatementBuilder buff = new StatementBuilder(); | ||
1491 | - buff.append(time).append(" ms: ").append(count).append(" * "); | ||
1492 | - if (prepared) { | ||
1493 | - buff.append("(Prepared) "); | ||
1494 | - } else { | ||
1495 | - buff.append("(Statement) "); | ||
1496 | - } | ||
1497 | - buff.append('('); | ||
1498 | - for (int p : params) { | ||
1499 | - buff.appendExceptFirst(", "); | ||
1500 | - buff.append(p == 0 ? "i" : "rnd"); | ||
1501 | - } | ||
1502 | - return buff.append(") ").append(sql).toString(); | ||
1503 | - } | ||
1504 | - | ||
1505 | - private String getCommandHistoryString() { | ||
1506 | - StringBuilder buff = new StringBuilder(); | ||
1507 | - ArrayList<String> history = session.getCommandHistory(); | ||
1508 | - buff.append("<table cellspacing=0 cellpadding=0>" + | ||
1509 | - "<tr><th></th><th>Command</th></tr>"); | ||
1510 | - for (int i = history.size() - 1; i >= 0; i--) { | ||
1511 | - String sql = history.get(i); | ||
1512 | - buff.append("<tr><td><a href=\"getHistory.do?id="). | ||
1513 | - append(i). | ||
1514 | - append("&jsessionid=${sessionId}\" target=\"h2query\" >"). | ||
1515 | - append("<img width=16 height=16 src=\"ico_write.gif\" " + | ||
1516 | - "onmouseover = \"this.className ='icon_hover'\" "). | ||
1517 | - append("onmouseout = \"this.className ='icon'\" " + | ||
1518 | - "class=\"icon\" alt=\"${text.resultEdit.edit}\" "). | ||
1519 | - append("title=\"${text.resultEdit.edit}\" border=\"1\"/></a>"). | ||
1520 | - append("</td><td>"). | ||
1521 | - append(PageParser.escapeHtml(sql)). | ||
1522 | - append("</td></tr>"); | ||
1523 | - } | ||
1524 | - buff.append("</table>"); | ||
1525 | - return buff.toString(); | ||
1526 | - } | ||
1527 | - | ||
1528 | - private static String getParameterResultSet(ParameterMetaData meta) | ||
1529 | - throws SQLException { | ||
1530 | - StringBuilder buff = new StringBuilder(); | ||
1531 | - if (meta == null) { | ||
1532 | - return "No parameter meta data"; | ||
1533 | - } | ||
1534 | - buff.append("<table cellspacing=0 cellpadding=0>"). | ||
1535 | - append("<tr><th>className</th><th>mode</th><th>type</th>"). | ||
1536 | - append("<th>typeName</th><th>precision</th><th>scale</th></tr>"); | ||
1537 | - for (int i = 0; i < meta.getParameterCount(); i++) { | ||
1538 | - buff.append("</tr><td>"). | ||
1539 | - append(meta.getParameterClassName(i + 1)). | ||
1540 | - append("</td><td>"). | ||
1541 | - append(meta.getParameterMode(i + 1)). | ||
1542 | - append("</td><td>"). | ||
1543 | - append(meta.getParameterType(i + 1)). | ||
1544 | - append("</td><td>"). | ||
1545 | - append(meta.getParameterTypeName(i + 1)). | ||
1546 | - append("</td><td>"). | ||
1547 | - append(meta.getPrecision(i + 1)). | ||
1548 | - append("</td><td>"). | ||
1549 | - append(meta.getScale(i + 1)). | ||
1550 | - append("</td></tr>"); | ||
1551 | - } | ||
1552 | - buff.append("</table>"); | ||
1553 | - return buff.toString(); | ||
1554 | - } | ||
1555 | - | ||
1556 | - private String getResultSet(String sql, ResultSet rs, boolean metadata, | ||
1557 | - boolean list, boolean edit, long time, boolean allowEdit) | ||
1558 | - throws SQLException { | ||
1559 | - int maxrows = getMaxrows(); | ||
1560 | - time = System.currentTimeMillis() - time; | ||
1561 | - StringBuilder buff = new StringBuilder(); | ||
1562 | - if (edit) { | ||
1563 | - buff.append("<form id=\"editing\" name=\"editing\" method=\"post\" " + | ||
1564 | - "action=\"editResult.do?jsessionid=${sessionId}\" " + | ||
1565 | - "id=\"mainForm\" target=\"h2result\">" + | ||
1566 | - "<input type=\"hidden\" name=\"op\" value=\"1\" />" + | ||
1567 | - "<input type=\"hidden\" name=\"row\" value=\"\" />" + | ||
1568 | - "<table cellspacing=0 cellpadding=0 id=\"editTable\">"); | ||
1569 | - } else { | ||
1570 | - buff.append("<table cellspacing=0 cellpadding=0>"); | ||
1571 | - } | ||
1572 | - if (metadata) { | ||
1573 | - SimpleResultSet r = new SimpleResultSet(); | ||
1574 | - r.addColumn("#", Types.INTEGER, 0, 0); | ||
1575 | - r.addColumn("label", Types.VARCHAR, 0, 0); | ||
1576 | - r.addColumn("catalog", Types.VARCHAR, 0, 0); | ||
1577 | - r.addColumn("schema", Types.VARCHAR, 0, 0); | ||
1578 | - r.addColumn("table", Types.VARCHAR, 0, 0); | ||
1579 | - r.addColumn("column", Types.VARCHAR, 0, 0); | ||
1580 | - r.addColumn("type", Types.INTEGER, 0, 0); | ||
1581 | - r.addColumn("typeName", Types.VARCHAR, 0, 0); | ||
1582 | - r.addColumn("class", Types.VARCHAR, 0, 0); | ||
1583 | - r.addColumn("precision", Types.INTEGER, 0, 0); | ||
1584 | - r.addColumn("scale", Types.INTEGER, 0, 0); | ||
1585 | - r.addColumn("displaySize", Types.INTEGER, 0, 0); | ||
1586 | - r.addColumn("autoIncrement", Types.BOOLEAN, 0, 0); | ||
1587 | - r.addColumn("caseSensitive", Types.BOOLEAN, 0, 0); | ||
1588 | - r.addColumn("currency", Types.BOOLEAN, 0, 0); | ||
1589 | - r.addColumn("nullable", Types.INTEGER, 0, 0); | ||
1590 | - r.addColumn("readOnly", Types.BOOLEAN, 0, 0); | ||
1591 | - r.addColumn("searchable", Types.BOOLEAN, 0, 0); | ||
1592 | - r.addColumn("signed", Types.BOOLEAN, 0, 0); | ||
1593 | - r.addColumn("writable", Types.BOOLEAN, 0, 0); | ||
1594 | - r.addColumn("definitelyWritable", Types.BOOLEAN, 0, 0); | ||
1595 | - ResultSetMetaData m = rs.getMetaData(); | ||
1596 | - for (int i = 1; i <= m.getColumnCount(); i++) { | ||
1597 | - r.addRow(i, | ||
1598 | - m.getColumnLabel(i), | ||
1599 | - m.getCatalogName(i), | ||
1600 | - m.getSchemaName(i), | ||
1601 | - m.getTableName(i), | ||
1602 | - m.getColumnName(i), | ||
1603 | - m.getColumnType(i), | ||
1604 | - m.getColumnTypeName(i), | ||
1605 | - m.getColumnClassName(i), | ||
1606 | - m.getPrecision(i), | ||
1607 | - m.getScale(i), | ||
1608 | - m.getColumnDisplaySize(i), | ||
1609 | - m.isAutoIncrement(i), | ||
1610 | - m.isCaseSensitive(i), | ||
1611 | - m.isCurrency(i), | ||
1612 | - m.isNullable(i), | ||
1613 | - m.isReadOnly(i), | ||
1614 | - m.isSearchable(i), | ||
1615 | - m.isSigned(i), | ||
1616 | - m.isWritable(i), | ||
1617 | - m.isDefinitelyWritable(i)); | ||
1618 | - } | ||
1619 | - rs = r; | ||
1620 | - } | ||
1621 | - ResultSetMetaData meta = rs.getMetaData(); | ||
1622 | - int columns = meta.getColumnCount(); | ||
1623 | - int rows = 0; | ||
1624 | - if (list) { | ||
1625 | - buff.append("<tr><th>Column</th><th>Data</th></tr><tr>"); | ||
1626 | - while (rs.next()) { | ||
1627 | - if (maxrows > 0 && rows >= maxrows) { | ||
1628 | - break; | ||
1629 | - } | ||
1630 | - rows++; | ||
1631 | - buff.append("<tr><td>Row #</td><td>"). | ||
1632 | - append(rows).append("</tr>"); | ||
1633 | - for (int i = 0; i < columns; i++) { | ||
1634 | - buff.append("<tr><td>"). | ||
1635 | - append(PageParser.escapeHtml(meta.getColumnLabel(i + 1))). | ||
1636 | - append("</td><td>"). | ||
1637 | - append(escapeData(rs, i + 1)). | ||
1638 | - append("</td></tr>"); | ||
1639 | - } | ||
1640 | - } | ||
1641 | - } else { | ||
1642 | - buff.append("<tr>"); | ||
1643 | - if (edit) { | ||
1644 | - buff.append("<th>${text.resultEdit.action}</th>"); | ||
1645 | - } | ||
1646 | - for (int i = 0; i < columns; i++) { | ||
1647 | - buff.append("<th>"). | ||
1648 | - append(PageParser.escapeHtml(meta.getColumnLabel(i + 1))). | ||
1649 | - append("</th>"); | ||
1650 | - } | ||
1651 | - buff.append("</tr>"); | ||
1652 | - while (rs.next()) { | ||
1653 | - if (maxrows > 0 && rows >= maxrows) { | ||
1654 | - break; | ||
1655 | - } | ||
1656 | - rows++; | ||
1657 | - buff.append("<tr>"); | ||
1658 | - if (edit) { | ||
1659 | - buff.append("<td>"). | ||
1660 | - append("<img onclick=\"javascript:editRow("). | ||
1661 | - append(rs.getRow()). | ||
1662 | - append(",'${sessionId}', '${text.resultEdit.save}', " + | ||
1663 | - "'${text.resultEdit.cancel}'"). | ||
1664 | - append(")\" width=16 height=16 src=\"ico_write.gif\" " + | ||
1665 | - "onmouseover = \"this.className ='icon_hover'\" " + | ||
1666 | - "onmouseout = \"this.className ='icon'\" " + | ||
1667 | - "class=\"icon\" alt=\"${text.resultEdit.edit}\" " + | ||
1668 | - "title=\"${text.resultEdit.edit}\" border=\"1\"/>"). | ||
1669 | - append("<a href=\"editResult.do?op=2&row="). | ||
1670 | - append(rs.getRow()). | ||
1671 | - append("&jsessionid=${sessionId}\" target=\"h2result\" >" + | ||
1672 | - "<img width=16 height=16 src=\"ico_remove.gif\" " + | ||
1673 | - "onmouseover = \"this.className ='icon_hover'\" " + | ||
1674 | - "onmouseout = \"this.className ='icon'\" " + | ||
1675 | - "class=\"icon\" alt=\"${text.resultEdit.delete}\" " + | ||
1676 | - "title=\"${text.resultEdit.delete}\" border=\"1\" /></a>"). | ||
1677 | - append("</td>"); | ||
1678 | - } | ||
1679 | - for (int i = 0; i < columns; i++) { | ||
1680 | - buff.append("<td>"). | ||
1681 | - append(escapeData(rs, i + 1)). | ||
1682 | - append("</td>"); | ||
1683 | - } | ||
1684 | - buff.append("</tr>"); | ||
1685 | - } | ||
1686 | - } | ||
1687 | - boolean isUpdatable = false; | ||
1688 | - try { | ||
1689 | - if (!session.getContents().isDB2()) { | ||
1690 | - isUpdatable = rs.getConcurrency() == ResultSet.CONCUR_UPDATABLE | ||
1691 | - && rs.getType() != ResultSet.TYPE_FORWARD_ONLY; | ||
1692 | - } | ||
1693 | - } catch (NullPointerException e) { | ||
1694 | - // ignore | ||
1695 | - // workaround for a JDBC-ODBC bridge problem | ||
1696 | - } | ||
1697 | - if (edit) { | ||
1698 | - ResultSet old = session.result; | ||
1699 | - if (old != null) { | ||
1700 | - old.close(); | ||
1701 | - } | ||
1702 | - session.result = rs; | ||
1703 | - } else { | ||
1704 | - rs.close(); | ||
1705 | - } | ||
1706 | - if (edit) { | ||
1707 | - buff.append("<tr><td>"). | ||
1708 | - append("<img onclick=\"javascript:editRow(-1, " + | ||
1709 | - "'${sessionId}', '${text.resultEdit.save}', '${text.resultEdit.cancel}'"). | ||
1710 | - append(")\" width=16 height=16 src=\"ico_add.gif\" " + | ||
1711 | - "onmouseover = \"this.className ='icon_hover'\" " + | ||
1712 | - "onmouseout = \"this.className ='icon'\" " + | ||
1713 | - "class=\"icon\" alt=\"${text.resultEdit.add}\" " + | ||
1714 | - "title=\"${text.resultEdit.add}\" border=\"1\"/>"). | ||
1715 | - append("</td>"); | ||
1716 | - for (int i = 0; i < columns; i++) { | ||
1717 | - buff.append("<td></td>"); | ||
1718 | - } | ||
1719 | - buff.append("</tr>"); | ||
1720 | - } | ||
1721 | - buff.append("</table>"); | ||
1722 | - if (edit) { | ||
1723 | - buff.append("</form>"); | ||
1724 | - } | ||
1725 | - if (rows == 0) { | ||
1726 | - buff.append("(${text.result.noRows}"); | ||
1727 | - } else if (rows == 1) { | ||
1728 | - buff.append("(${text.result.1row}"); | ||
1729 | - } else { | ||
1730 | - buff.append('(').append(rows).append(" ${text.result.rows}"); | ||
1731 | - } | ||
1732 | - buff.append(", "); | ||
1733 | - time = System.currentTimeMillis() - time; | ||
1734 | - buff.append(time).append(" ms)"); | ||
1735 | - if (!edit && isUpdatable && allowEdit) { | ||
1736 | - buff.append("<br /><br />" + | ||
1737 | - "<form name=\"editResult\" method=\"post\" " + | ||
1738 | - "action=\"query.do?jsessionid=${sessionId}\" target=\"h2result\">" + | ||
1739 | - "<input type=\"submit\" class=\"button\" " + | ||
1740 | - "value=\"${text.resultEdit.editResult}\" />" + | ||
1741 | - "<input type=\"hidden\" name=\"sql\" value=\"@edit "). | ||
1742 | - append(PageParser.escapeHtmlData(sql)). | ||
1743 | - append("\" /></form>"); | ||
1744 | - } | ||
1745 | - return buff.toString(); | ||
1746 | - } | ||
1747 | - | ||
1748 | - /** | ||
1749 | - * Save the current connection settings to the properties file. | ||
1750 | - * | ||
1751 | - * @return the file to open afterwards | ||
1752 | - */ | ||
1753 | - private String settingSave() { | ||
1754 | - ConnectionInfo info = new ConnectionInfo(); | ||
1755 | - info.name = attributes.getProperty("name", ""); | ||
1756 | - info.driver = attributes.getProperty("driver", ""); | ||
1757 | - info.url = attributes.getProperty("url", ""); | ||
1758 | - info.user = attributes.getProperty("user", ""); | ||
1759 | - server.updateSetting(info); | ||
1760 | - attributes.put("setting", info.name); | ||
1761 | - server.saveProperties(null); | ||
1762 | - return "index.do"; | ||
1763 | - } | ||
1764 | - | ||
1765 | - private static String escapeData(ResultSet rs, int columnIndex) | ||
1766 | - throws SQLException { | ||
1767 | - String d = rs.getString(columnIndex); | ||
1768 | - if (d == null) { | ||
1769 | - return "<i>null</i>"; | ||
1770 | - } else if (d.length() > 100000) { | ||
1771 | - String s; | ||
1772 | - if (isBinary(rs.getMetaData().getColumnType(columnIndex))) { | ||
1773 | - s = PageParser.escapeHtml(d.substring(0, 6)) + | ||
1774 | - "... (" + (d.length() / 2) + " ${text.result.bytes})"; | ||
1775 | - } else { | ||
1776 | - s = PageParser.escapeHtml(d.substring(0, 100)) + | ||
1777 | - "... (" + d.length() + " ${text.result.characters})"; | ||
1778 | - } | ||
1779 | - return "<div style='display: none'>=+</div>" + s; | ||
1780 | - } else if (d.equals("null") || d.startsWith("= ") || d.startsWith("=+")) { | ||
1781 | - return "<div style='display: none'>= </div>" + PageParser.escapeHtml(d); | ||
1782 | - } else if (d.equals("")) { | ||
1783 | - // PageParser.escapeHtml replaces "" with a non-breaking space | ||
1784 | - return ""; | ||
1785 | - } | ||
1786 | - return PageParser.escapeHtml(d); | ||
1787 | - } | ||
1788 | - | ||
1789 | - private static boolean isBinary(int sqlType) { | ||
1790 | - switch (sqlType) { | ||
1791 | - case Types.BINARY: | ||
1792 | - case Types.BLOB: | ||
1793 | - case Types.JAVA_OBJECT: | ||
1794 | - case Types.LONGVARBINARY: | ||
1795 | - case Types.OTHER: | ||
1796 | - case Types.VARBINARY: | ||
1797 | - return true; | ||
1798 | - } | ||
1799 | - return false; | ||
1800 | - } | ||
1801 | - | ||
1802 | - private void unescapeData(String x, ResultSet rs, int columnIndex) | ||
1803 | - throws SQLException { | ||
1804 | - if (x.equals("null")) { | ||
1805 | - rs.updateNull(columnIndex); | ||
1806 | - return; | ||
1807 | - } else if (x.startsWith("=+")) { | ||
1808 | - // don't update | ||
1809 | - return; | ||
1810 | - } else if (x.equals("=*")) { | ||
1811 | - // set an appropriate default value | ||
1812 | - int type = rs.getMetaData().getColumnType(columnIndex); | ||
1813 | - switch (type) { | ||
1814 | - case Types.TIME: | ||
1815 | - rs.updateString(columnIndex, "12:00:00"); | ||
1816 | - break; | ||
1817 | - case Types.TIMESTAMP: | ||
1818 | - case Types.DATE: | ||
1819 | - rs.updateString(columnIndex, "2001-01-01"); | ||
1820 | - break; | ||
1821 | - default: | ||
1822 | - rs.updateString(columnIndex, "1"); | ||
1823 | - break; | ||
1824 | - } | ||
1825 | - return; | ||
1826 | - } else if (x.startsWith("= ")) { | ||
1827 | - x = x.substring(2); | ||
1828 | - } | ||
1829 | - ResultSetMetaData meta = rs.getMetaData(); | ||
1830 | - int type = meta.getColumnType(columnIndex); | ||
1831 | - if (session.getContents().isH2()) { | ||
1832 | - rs.updateString(columnIndex, x); | ||
1833 | - return; | ||
1834 | - } | ||
1835 | - switch (type) { | ||
1836 | - case Types.BIGINT: | ||
1837 | - rs.updateLong(columnIndex, Long.decode(x)); | ||
1838 | - break; | ||
1839 | - case Types.DECIMAL: | ||
1840 | - rs.updateBigDecimal(columnIndex, new BigDecimal(x)); | ||
1841 | - break; | ||
1842 | - case Types.DOUBLE: | ||
1843 | - case Types.FLOAT: | ||
1844 | - rs.updateDouble(columnIndex, Double.parseDouble(x)); | ||
1845 | - break; | ||
1846 | - case Types.REAL: | ||
1847 | - rs.updateFloat(columnIndex, Float.parseFloat(x)); | ||
1848 | - break; | ||
1849 | - case Types.INTEGER: | ||
1850 | - rs.updateInt(columnIndex, Integer.decode(x)); | ||
1851 | - break; | ||
1852 | - case Types.TINYINT: | ||
1853 | - rs.updateShort(columnIndex, Short.decode(x)); | ||
1854 | - break; | ||
1855 | - default: | ||
1856 | - rs.updateString(columnIndex, x); | ||
1857 | - } | ||
1858 | - } | ||
1859 | - | ||
1860 | - private String settingRemove() { | ||
1861 | - String setting = attributes.getProperty("name", ""); | ||
1862 | - server.removeSetting(setting); | ||
1863 | - ArrayList<ConnectionInfo> settings = server.getSettings(); | ||
1864 | - if (settings.size() > 0) { | ||
1865 | - attributes.put("setting", settings.get(0)); | ||
1866 | - } | ||
1867 | - server.saveProperties(null); | ||
1868 | - return "index.do"; | ||
1869 | - } | ||
1870 | - | ||
1871 | - /** | ||
1872 | - * Get the current mime type. | ||
1873 | - * | ||
1874 | - * @return the mime type | ||
1875 | - */ | ||
1876 | - String getMimeType() { | ||
1877 | - return mimeType; | ||
1878 | - } | ||
1879 | - | ||
1880 | - boolean getCache() { | ||
1881 | - return cache; | ||
1882 | - } | ||
1883 | - | ||
1884 | - WebSession getSession() { | ||
1885 | - return session; | ||
1886 | - } | ||
1887 | - | ||
1888 | - private void trace(String s) { | ||
1889 | - server.trace(s); | ||
1890 | - } | ||
1891 | - | ||
1892 | -} | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
1 | -package org.legrog.util; | ||
2 | -// Code copied from org.h2.server.web.WebServer | ||
3 | -/* | ||
4 | - * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0, | ||
5 | - * and the EPL 1.0 (http://h2database.com/html/license.html). | ||
6 | - * Initial Developer: H2 Group | ||
7 | - */ | ||
8 | -//package org.h2.server.web; | ||
9 | - | ||
10 | - import java.io.File; | ||
11 | - import java.io.IOException; | ||
12 | - import java.io.InputStream; | ||
13 | - import java.io.OutputStream; | ||
14 | - import java.net.ServerSocket; | ||
15 | - import java.net.Socket; | ||
16 | - import java.sql.Connection; | ||
17 | - import java.sql.SQLException; | ||
18 | - import java.text.SimpleDateFormat; | ||
19 | - import java.util.ArrayList; | ||
20 | - import java.util.Collections; | ||
21 | - import java.util.HashMap; | ||
22 | - import java.util.HashSet; | ||
23 | - import java.util.Locale; | ||
24 | - import java.util.Map; | ||
25 | - import java.util.Map.Entry; | ||
26 | - import java.util.Properties; | ||
27 | - import java.util.Set; | ||
28 | - import java.util.TimeZone; | ||
29 | - | ||
30 | - import org.h2.engine.Constants; | ||
31 | - import org.h2.engine.SysProperties; | ||
32 | - import org.h2.message.DbException; | ||
33 | - import org.h2.server.Service; | ||
34 | - import org.h2.server.ShutdownHandler; | ||
35 | - import org.h2.store.fs.FileUtils; | ||
36 | - import org.h2.util.JdbcUtils; | ||
37 | - import org.h2.util.MathUtils; | ||
38 | - import org.h2.util.NetUtils; | ||
39 | - import org.h2.util.New; | ||
40 | - import org.h2.util.SortedProperties; | ||
41 | - import org.h2.util.StringUtils; | ||
42 | - import org.h2.util.Tool; | ||
43 | - import 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 | - */ | ||
49 | -public class WebServer implements Service { | ||
50 | - | ||
51 | - static final String[][] LANGUAGES = { | ||
52 | - { "cs", "\u010ce\u0161tina" }, | ||
53 | - { "de", "Deutsch" }, | ||
54 | - { "en", "English" }, | ||
55 | - { "es", "Espa\u00f1ol" }, | ||
56 | - { "fr", "Fran\u00e7ais" }, | ||
57 | - { "hu", "Magyar"}, | ||
58 | - { "ko", "\ud55c\uad6d\uc5b4"}, | ||
59 | - { "in", "Indonesia"}, | ||
60 | - { "it", "Italiano"}, | ||
61 | - { "ja", "\u65e5\u672c\u8a9e"}, | ||
62 | - { "nl", "Nederlands"}, | ||
63 | - { "pl", "Polski"}, | ||
64 | - { "pt_BR", "Portugu\u00eas (Brasil)"}, | ||
65 | - { "pt_PT", "Portugu\u00eas (Europeu)"}, | ||
66 | - { "ru", "\u0440\u0443\u0441\u0441\u043a\u0438\u0439"}, | ||
67 | - { "sk", "Slovensky"}, | ||
68 | - { "tr", "T\u00fcrk\u00e7e"}, | ||
69 | - { "uk", "\u0423\u043A\u0440\u0430\u0457\u043D\u0441\u044C\u043A\u0430"}, | ||
70 | - { "zh_CN", "\u4e2d\u6587 (\u7b80\u4f53)"}, | ||
71 | - { "zh_TW", "\u4e2d\u6587 (\u7e41\u9ad4)"}, | ||
72 | - }; | ||
73 | - | ||
74 | - private static final String COMMAND_HISTORY = "commandHistory"; | ||
75 | - | ||
76 | - private static final String DEFAULT_LANGUAGE = "en"; | ||
77 | - | ||
78 | - private static final String[] GENERIC = { | ||
79 | - "Generic JNDI Data Source|javax.naming.InitialContext|" + | ||
80 | - "java:comp/env/jdbc/Test|sa", | ||
81 | - "Generic Firebird Server|org.firebirdsql.jdbc.FBDriver|" + | ||
82 | - "jdbc:firebirdsql:localhost:c:/temp/firebird/test|sysdba", | ||
83 | - "Generic SQLite|org.sqlite.JDBC|" + | ||
84 | - "jdbc:sqlite:test|sa", | ||
85 | - "Generic DB2|com.ibm.db2.jcc.DB2Driver|" + | ||
86 | - "jdbc:db2://localhost/test|" , | ||
87 | - "Generic Oracle|oracle.jdbc.driver.OracleDriver|" + | ||
88 | - "jdbc:oracle:thin:@localhost:1521:XE|sa" , | ||
89 | - "Generic MS SQL Server 2000|com.microsoft.jdbc.sqlserver.SQLServerDriver|" + | ||
90 | - "jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=sqlexpress|sa", | ||
91 | - "Generic MS SQL Server 2005|com.microsoft.sqlserver.jdbc.SQLServerDriver|" + | ||
92 | - "jdbc:sqlserver://localhost;DatabaseName=test|sa", | ||
93 | - "Generic PostgreSQL|org.postgresql.Driver|" + | ||
94 | - "jdbc:postgresql:test|" , | ||
95 | - "Generic MySQL|com.mysql.jdbc.Driver|" + | ||
96 | - "jdbc:mysql://localhost:3306/test|" , | ||
97 | - "Generic HSQLDB|org.hsqldb.jdbcDriver|" + | ||
98 | - "jdbc:hsqldb:test;hsqldb.default_table_type=cached|sa" , | ||
99 | - "Generic Derby (Server)|org.apache.derby.jdbc.ClientDriver|" + | ||
100 | - "jdbc:derby://localhost:1527/test;create=true|sa", | ||
101 | - "Generic Derby (Embedded)|org.apache.derby.jdbc.EmbeddedDriver|" + | ||
102 | - "jdbc:derby:test;create=true|sa", | ||
103 | - "Generic H2 (Server)|org.h2.Driver|" + | ||
104 | - "jdbc:h2:tcp://localhost/~/test|sa", | ||
105 | - // this will be listed on top for new installations | ||
106 | - "Generic H2 (Embedded)|org.h2.Driver|" + | ||
107 | - "jdbc:h2:~/test|sa", | ||
108 | - }; | ||
109 | - | ||
110 | - private static int ticker; | ||
111 | - | ||
112 | - /** | ||
113 | - * The session timeout (the default is 30 minutes). | ||
114 | - */ | ||
115 | - private static final long SESSION_TIMEOUT = SysProperties.CONSOLE_TIMEOUT; | ||
116 | - | ||
117 | -// public static void main(String... args) throws IOException { | ||
118 | -// String s = IOUtils.readStringAndClose(new java.io.FileReader( | ||
119 | -// // "src/main/org/h2/server/web/res/_text_cs.prop"), -1); | ||
120 | -// "src/main/org/h2/res/_messages_cs.prop"), -1); | ||
121 | -// System.out.println(StringUtils.javaEncode("...")); | ||
122 | -// String[] list = Locale.getISOLanguages(); | ||
123 | -// for (int i = 0; i < list.length; i++) { | ||
124 | -// System.out.print(list[i] + " "); | ||
125 | -// } | ||
126 | -// System.out.println(); | ||
127 | -// String l = "de"; | ||
128 | -// String lang = new java.util.Locale(l). | ||
129 | -// getDisplayLanguage(new java.util.Locale(l)); | ||
130 | -// System.out.println(new java.util.Locale(l).getDisplayLanguage()); | ||
131 | -// System.out.println(lang); | ||
132 | -// java.util.Locale.CHINESE.getDisplayLanguage(java.util.Locale.CHINESE); | ||
133 | -// for (int i = 0; i < lang.length(); i++) { | ||
134 | -// System.out.println(Integer.toHexString(lang.charAt(i)) + " "); | ||
135 | -// } | ||
136 | -// } | ||
137 | - | ||
138 | - // private URLClassLoader urlClassLoader; | ||
139 | - private int port; | ||
140 | - private boolean allowOthers; | ||
141 | - private boolean isDaemon; | ||
142 | - private final Set<WebThread> running = | ||
143 | - Collections.synchronizedSet(new HashSet<WebThread>()); | ||
144 | - private boolean ssl; | ||
145 | - private final HashMap<String, ConnectionInfo> connInfoMap = New.hashMap(); | ||
146 | - | ||
147 | - private long lastTimeoutCheck; | ||
148 | - private final HashMap<String, WebSession> sessions = New.hashMap(); | ||
149 | - private final HashSet<String> languages = New.hashSet(); | ||
150 | - private String startDateTime; | ||
151 | - private ServerSocket serverSocket; | ||
152 | - private String url; | ||
153 | - private ShutdownHandler shutdownHandler; | ||
154 | - private Thread listenerThread; | ||
155 | - private boolean ifExists; | ||
156 | - private boolean trace; | ||
157 | - private TranslateThread translateThread; | ||
158 | - private boolean allowChunked = true; | ||
159 | - private String serverPropertiesDir = Constants.SERVER_PROPERTIES_DIR; | ||
160 | - // null means the history is not allowed to be stored | ||
161 | - private String commandHistoryString; | ||
162 | - | ||
163 | - /** | ||
164 | - * Read the given file from the file system or from the resources. | ||
165 | - * | ||
166 | - * @param file the file name | ||
167 | - * @return the data | ||
168 | - */ | ||
169 | - byte[] getFile(String file) throws IOException { | ||
170 | - trace("getFile <" + file + ">"); | ||
171 | - byte[] data = Utils.getResource("/org/h2/server/web/res/" + file); | ||
172 | - if (data == null) { | ||
173 | - trace(" null"); | ||
174 | - } else { | ||
175 | - trace(" size=" + data.length); | ||
176 | - } | ||
177 | - return data; | ||
178 | - } | ||
179 | - | ||
180 | - /** | ||
181 | - * Check if this is a simple name (only contains '.', '-', '_', letters, or | ||
182 | - * digits). | ||
183 | - * | ||
184 | - * @param s the string | ||
185 | - * @return true if it's a simple name | ||
186 | - */ | ||
187 | - static boolean isSimpleName(String s) { | ||
188 | - for (char c : s.toCharArray()) { | ||
189 | - if (c != '.' && c != '_' && c != '-' && !Character.isLetterOrDigit(c)) { | ||
190 | - return false; | ||
191 | - } | ||
192 | - } | ||
193 | - return true; | ||
194 | - } | ||
195 | - | ||
196 | - /** | ||
197 | - * Remove this web thread from the set of running threads. | ||
198 | - * | ||
199 | - * @param t the thread to remove | ||
200 | - */ | ||
201 | - synchronized void remove(WebThread t) { | ||
202 | - running.remove(t); | ||
203 | - } | ||
204 | - | ||
205 | - private static String generateSessionId() { | ||
206 | - byte[] buff = MathUtils.secureRandomBytes(16); | ||
207 | - return StringUtils.convertBytesToHex(buff); | ||
208 | - } | ||
209 | - | ||
210 | - /** | ||
211 | - * Get the web session object for the given session id. | ||
212 | - * | ||
213 | - * @param sessionId the session id | ||
214 | - * @return the web session or null | ||
215 | - */ | ||
216 | - WebSession getSession(String sessionId) { | ||
217 | - long now = System.currentTimeMillis(); | ||
218 | - if (lastTimeoutCheck + SESSION_TIMEOUT < now) { | ||
219 | - for (String id : New.arrayList(sessions.keySet())) { | ||
220 | - WebSession session = sessions.get(id); | ||
221 | - if (session.lastAccess + SESSION_TIMEOUT < now) { | ||
222 | - trace("timeout for " + id); | ||
223 | - sessions.remove(id); | ||
224 | - } | ||
225 | - } | ||
226 | - lastTimeoutCheck = now; | ||
227 | - } | ||
228 | - WebSession session = sessions.get(sessionId); | ||
229 | - if (session != null) { | ||
230 | - session.lastAccess = System.currentTimeMillis(); | ||
231 | - } | ||
232 | - return session; | ||
233 | - } | ||
234 | - | ||
235 | - /** | ||
236 | - * Create a new web session id and object. | ||
237 | - * | ||
238 | - * @param hostAddr the host address | ||
239 | - * @return the web session object | ||
240 | - */ | ||
241 | - WebSession createNewSession(String hostAddr) { | ||
242 | - String newId; | ||
243 | - do { | ||
244 | - newId = generateSessionId(); | ||
245 | - } while (sessions.get(newId) != null); | ||
246 | - WebSession session = new WebSession(this); | ||
247 | - session.lastAccess = System.currentTimeMillis(); | ||
248 | - session.put("sessionId", newId); | ||
249 | - session.put("ip", hostAddr); | ||
250 | - session.put("language", DEFAULT_LANGUAGE); | ||
251 | - session.put("frame-border", "0"); | ||
252 | - session.put("frameset-border", "4"); | ||
253 | - sessions.put(newId, session); | ||
254 | - // always read the english translation, | ||
255 | - // so that untranslated text appears at least in english | ||
256 | - readTranslations(session, DEFAULT_LANGUAGE); | ||
257 | - return getSession(newId); | ||
258 | - } | ||
259 | - | ||
260 | - String getStartDateTime() { | ||
261 | - if (startDateTime == null) { | ||
262 | - SimpleDateFormat format = new SimpleDateFormat( | ||
263 | - "EEE, d MMM yyyy HH:mm:ss z", new Locale("en", "")); | ||
264 | - format.setTimeZone(TimeZone.getTimeZone("GMT")); | ||
265 | - startDateTime = format.format(System.currentTimeMillis()); | ||
266 | - } | ||
267 | - return startDateTime; | ||
268 | - } | ||
269 | - | ||
270 | - @Override | ||
271 | - public void init(String... args) { | ||
272 | - // set the serverPropertiesDir, because it's used in loadProperties() | ||
273 | - for (int i = 0; args != null && i < args.length; i++) { | ||
274 | - if ("-properties".equals(args[i])) { | ||
275 | - serverPropertiesDir = args[++i]; | ||
276 | - } | ||
277 | - } | ||
278 | - Properties prop = loadProperties(); | ||
279 | - port = SortedProperties.getIntProperty(prop, | ||
280 | - "webPort", Constants.DEFAULT_HTTP_PORT); | ||
281 | - ssl = SortedProperties.getBooleanProperty(prop, | ||
282 | - "webSSL", false); | ||
283 | - allowOthers = SortedProperties.getBooleanProperty(prop, | ||
284 | - "webAllowOthers", false); | ||
285 | - commandHistoryString = prop.getProperty(COMMAND_HISTORY); | ||
286 | - for (int i = 0; args != null && i < args.length; i++) { | ||
287 | - String a = args[i]; | ||
288 | - if (Tool.isOption(a, "-webPort")) { | ||
289 | - port = Integer.decode(args[++i]); | ||
290 | - } else if (Tool.isOption(a, "-webSSL")) { | ||
291 | - ssl = true; | ||
292 | - } else if (Tool.isOption(a, "-webAllowOthers")) { | ||
293 | - allowOthers = true; | ||
294 | - } else if (Tool.isOption(a, "-webDaemon")) { | ||
295 | - isDaemon = true; | ||
296 | - } else if (Tool.isOption(a, "-baseDir")) { | ||
297 | - String baseDir = args[++i]; | ||
298 | - SysProperties.setBaseDir(baseDir); | ||
299 | - } else if (Tool.isOption(a, "-ifExists")) { | ||
300 | - ifExists = true; | ||
301 | - } else if (Tool.isOption(a, "-properties")) { | ||
302 | - // already set | ||
303 | - i++; | ||
304 | - } else if (Tool.isOption(a, "-trace")) { | ||
305 | - trace = true; | ||
306 | - } | ||
307 | - } | ||
308 | -// if (driverList != null) { | ||
309 | -// try { | ||
310 | -// String[] drivers = | ||
311 | -// StringUtils.arraySplit(driverList, ',', false); | ||
312 | -// URL[] urls = new URL[drivers.length]; | ||
313 | -// for(int i=0; i<drivers.length; i++) { | ||
314 | -// urls[i] = new URL(drivers[i]); | ||
315 | -// } | ||
316 | -// urlClassLoader = URLClassLoader.newInstance(urls); | ||
317 | -// } catch (MalformedURLException e) { | ||
318 | -// TraceSystem.traceThrowable(e); | ||
319 | -// } | ||
320 | -// } | ||
321 | - for (String[] lang : LANGUAGES) { | ||
322 | - languages.add(lang[0]); | ||
323 | - } | ||
324 | - updateURL(); | ||
325 | - } | ||
326 | - | ||
327 | - @Override | ||
328 | - public String getURL() { | ||
329 | - updateURL(); | ||
330 | - return url; | ||
331 | - } | ||
332 | - | ||
333 | - private void updateURL() { | ||
334 | - try { | ||
335 | - url = (ssl ? "https" : "http") + "://" + | ||
336 | - NetUtils.getLocalAddress() + ":" + port; | ||
337 | - } catch (NoClassDefFoundError e) { | ||
338 | - // Google App Engine does not allow java.net.InetAddress | ||
339 | - } | ||
340 | - } | ||
341 | - | ||
342 | - @Override | ||
343 | - public void start() { | ||
344 | - serverSocket = NetUtils.createServerSocket(port, ssl); | ||
345 | - port = serverSocket.getLocalPort(); | ||
346 | - updateURL(); | ||
347 | - } | ||
348 | - | ||
349 | - @Override | ||
350 | - public void listen() { | ||
351 | - this.listenerThread = Thread.currentThread(); | ||
352 | - try { | ||
353 | - while (serverSocket != null) { | ||
354 | - Socket s = serverSocket.accept(); | ||
355 | - WebThread c = new WebThread(s, this); | ||
356 | - running.add(c); | ||
357 | - c.start(); | ||
358 | - } | ||
359 | - } catch (Exception e) { | ||
360 | - trace(e.toString()); | ||
361 | - } | ||
362 | - } | ||
363 | - | ||
364 | - @Override | ||
365 | - public boolean isRunning(boolean traceError) { | ||
366 | - if (serverSocket == null) { | ||
367 | - return false; | ||
368 | - } | ||
369 | - try { | ||
370 | - Socket s = NetUtils.createLoopbackSocket(port, ssl); | ||
371 | - s.close(); | ||
372 | - return true; | ||
373 | - } catch (Exception e) { | ||
374 | - if (traceError) { | ||
375 | - traceError(e); | ||
376 | - } | ||
377 | - return false; | ||
378 | - } | ||
379 | - } | ||
380 | - | ||
381 | - public boolean isStopped() { | ||
382 | - return serverSocket == null; | ||
383 | - } | ||
384 | - | ||
385 | - @Override | ||
386 | - public void stop() { | ||
387 | - if (serverSocket != null) { | ||
388 | - try { | ||
389 | - serverSocket.close(); | ||
390 | - } catch (IOException e) { | ||
391 | - traceError(e); | ||
392 | - } | ||
393 | - serverSocket = null; | ||
394 | - } | ||
395 | - if (listenerThread != null) { | ||
396 | - try { | ||
397 | - listenerThread.join(1000); | ||
398 | - } catch (InterruptedException e) { | ||
399 | - DbException.traceThrowable(e); | ||
400 | - } | ||
401 | - } | ||
402 | - // TODO server: using a boolean 'now' argument? a timeout? | ||
403 | - for (WebSession session : New.arrayList(sessions.values())) { | ||
404 | - session.close(); | ||
405 | - } | ||
406 | - for (WebThread c : New.arrayList(running)) { | ||
407 | - try { | ||
408 | - c.stopNow(); | ||
409 | - c.join(100); | ||
410 | - } catch (Exception e) { | ||
411 | - traceError(e); | ||
412 | - } | ||
413 | - } | ||
414 | - } | ||
415 | - | ||
416 | - /** | ||
417 | - * Write trace information if trace is enabled. | ||
418 | - * | ||
419 | - * @param s the message to write | ||
420 | - */ | ||
421 | - void trace(String s) { | ||
422 | - if (trace) { | ||
423 | - System.out.println(s); | ||
424 | - } | ||
425 | - } | ||
426 | - | ||
427 | - /** | ||
428 | - * Write the stack trace if trace is enabled. | ||
429 | - * | ||
430 | - * @param e the exception | ||
431 | - */ | ||
432 | - void traceError(Throwable e) { | ||
433 | - if (trace) { | ||
434 | - e.printStackTrace(); | ||
435 | - } | ||
436 | - } | ||
437 | - | ||
438 | - /** | ||
439 | - * Check if this language is supported / translated. | ||
440 | - * | ||
441 | - * @param language the language | ||
442 | - * @return true if a translation is available | ||
443 | - */ | ||
444 | - boolean supportsLanguage(String language) { | ||
445 | - return languages.contains(language); | ||
446 | - } | ||
447 | - | ||
448 | - /** | ||
449 | - * Read the translation for this language and save them in the 'text' | ||
450 | - * property of this session. | ||
451 | - * | ||
452 | - * @param session the session | ||
453 | - * @param language the language | ||
454 | - */ | ||
455 | - void readTranslations(WebSession session, String language) { | ||
456 | - Properties text = new Properties(); | ||
457 | - try { | ||
458 | - trace("translation: "+language); | ||
459 | - byte[] trans = getFile("_text_"+language+".prop"); | ||
460 | - trace(" "+new String(trans)); | ||
461 | - text = SortedProperties.fromLines(new String(trans, Constants.UTF8)); | ||
462 | - // remove starting # (if not translated yet) | ||
463 | - for (Entry<Object, Object> entry : text.entrySet()) { | ||
464 | - String value = (String) entry.getValue(); | ||
465 | - if (value.startsWith("#")) { | ||
466 | - entry.setValue(value.substring(1)); | ||
467 | - } | ||
468 | - } | ||
469 | - } catch (IOException e) { | ||
470 | - DbException.traceThrowable(e); | ||
471 | - } | ||
472 | - session.put("text", new HashMap<Object, Object>(text)); | ||
473 | - } | ||
474 | - | ||
475 | - ArrayList<HashMap<String, Object>> getSessions() { | ||
476 | - ArrayList<HashMap<String, Object>> list = New.arrayList(); | ||
477 | - for (WebSession s : sessions.values()) { | ||
478 | - list.add(s.getInfo()); | ||
479 | - } | ||
480 | - return list; | ||
481 | - } | ||
482 | - | ||
483 | - @Override | ||
484 | - public String getType() { | ||
485 | - return "Web Console"; | ||
486 | - } | ||
487 | - | ||
488 | - @Override | ||
489 | - public String getName() { | ||
490 | - return "H2 Console Server"; | ||
491 | - } | ||
492 | - | ||
493 | - void setAllowOthers(boolean b) { | ||
494 | - allowOthers = b; | ||
495 | - } | ||
496 | - | ||
497 | - @Override | ||
498 | - public boolean getAllowOthers() { | ||
499 | - return allowOthers; | ||
500 | - } | ||
501 | - | ||
502 | - void setSSL(boolean b) { | ||
503 | - ssl = b; | ||
504 | - } | ||
505 | - | ||
506 | - void setPort(int port) { | ||
507 | - this.port = port; | ||
508 | - } | ||
509 | - | ||
510 | - boolean getSSL() { | ||
511 | - return ssl; | ||
512 | - } | ||
513 | - | ||
514 | - @Override | ||
515 | - public int getPort() { | ||
516 | - return port; | ||
517 | - } | ||
518 | - | ||
519 | - public boolean isCommandHistoryAllowed() { | ||
520 | - return commandHistoryString != null; | ||
521 | - } | ||
522 | - | ||
523 | - public void setCommandHistoryAllowed(boolean allowed) { | ||
524 | - if (allowed) { | ||
525 | - if (commandHistoryString == null) { | ||
526 | - commandHistoryString = ""; | ||
527 | - } | ||
528 | - } else { | ||
529 | - commandHistoryString = null; | ||
530 | - } | ||
531 | - } | ||
532 | - | ||
533 | - public ArrayList<String> getCommandHistoryList() { | ||
534 | - ArrayList<String> result = New.arrayList(); | ||
535 | - if (commandHistoryString == null) { | ||
536 | - return result; | ||
537 | - } | ||
538 | - | ||
539 | - // Split the commandHistoryString on non-escaped semicolons | ||
540 | - // and unescape it. | ||
541 | - StringBuilder sb = new StringBuilder(); | ||
542 | - for (int end = 0;; end++) { | ||
543 | - if (end == commandHistoryString.length() || | ||
544 | - commandHistoryString.charAt(end) == ';') { | ||
545 | - if (sb.length() > 0) { | ||
546 | - result.add(sb.toString()); | ||
547 | - sb.delete(0, sb.length()); | ||
548 | - } | ||
549 | - if (end == commandHistoryString.length()) { | ||
550 | - break; | ||
551 | - } | ||
552 | - } else if (commandHistoryString.charAt(end) == '\\' && | ||
553 | - end < commandHistoryString.length() - 1) { | ||
554 | - sb.append(commandHistoryString.charAt(++end)); | ||
555 | - } else { | ||
556 | - sb.append(commandHistoryString.charAt(end)); | ||
557 | - } | ||
558 | - } | ||
559 | - return result; | ||
560 | - } | ||
561 | - | ||
562 | - /** | ||
563 | - * Save the command history to the properties file. | ||
564 | - * | ||
565 | - * @param commandHistory the history | ||
566 | - */ | ||
567 | - public void saveCommandHistoryList(ArrayList<String> commandHistory) { | ||
568 | - StringBuilder sb = new StringBuilder(); | ||
569 | - for (String s : commandHistory) { | ||
570 | - if (sb.length() > 0) { | ||
571 | - sb.append(';'); | ||
572 | - } | ||
573 | - sb.append(s.replace("\\", "\\\\").replace(";", "\\;")); | ||
574 | - } | ||
575 | - commandHistoryString = sb.toString(); | ||
576 | - saveProperties(null); | ||
577 | - } | ||
578 | - | ||
579 | - /** | ||
580 | - * Get the connection information for this setting. | ||
581 | - * | ||
582 | - * @param name the setting name | ||
583 | - * @return the connection information | ||
584 | - */ | ||
585 | - ConnectionInfo getSetting(String name) { | ||
586 | - return connInfoMap.get(name); | ||
587 | - } | ||
588 | - | ||
589 | - /** | ||
590 | - * Update a connection information setting. | ||
591 | - * | ||
592 | - * @param info the connection information | ||
593 | - */ | ||
594 | - void updateSetting(ConnectionInfo info) { | ||
595 | - connInfoMap.put(info.name, info); | ||
596 | - info.lastAccess = ticker++; | ||
597 | - } | ||
598 | - | ||
599 | - /** | ||
600 | - * Remove a connection information setting from the list | ||
601 | - * | ||
602 | - * @param name the setting to remove | ||
603 | - */ | ||
604 | - void removeSetting(String name) { | ||
605 | - connInfoMap.remove(name); | ||
606 | - } | ||
607 | - | ||
608 | - private Properties loadProperties() { | ||
609 | - try { | ||
610 | - if ("null".equals(serverPropertiesDir)) { | ||
611 | - return new Properties(); | ||
612 | - } | ||
613 | - return SortedProperties.loadProperties( | ||
614 | - serverPropertiesDir + "/" + Constants.SERVER_PROPERTIES_NAME); | ||
615 | - } catch (Exception e) { | ||
616 | - DbException.traceThrowable(e); | ||
617 | - return new Properties(); | ||
618 | - } | ||
619 | - } | ||
620 | - | ||
621 | - /** | ||
622 | - * Get the list of connection information setting names. | ||
623 | - * | ||
624 | - * @return the connection info names | ||
625 | - */ | ||
626 | - String[] getSettingNames() { | ||
627 | - ArrayList<ConnectionInfo> list = getSettings(); | ||
628 | - String[] names = new String[list.size()]; | ||
629 | - for (int i = 0; i < list.size(); i++) { | ||
630 | - names[i] = list.get(i).name; | ||
631 | - } | ||
632 | - return names; | ||
633 | - } | ||
634 | - | ||
635 | - /** | ||
636 | - * Get the list of connection info objects. | ||
637 | - * | ||
638 | - * @return the list | ||
639 | - */ | ||
640 | - synchronized ArrayList<ConnectionInfo> getSettings() { | ||
641 | - ArrayList<ConnectionInfo> settings = New.arrayList(); | ||
642 | - if (connInfoMap.size() == 0) { | ||
643 | - Properties prop = loadProperties(); | ||
644 | - if (prop.size() == 0) { | ||
645 | - for (String gen : GENERIC) { | ||
646 | - ConnectionInfo info = new ConnectionInfo(gen); | ||
647 | - settings.add(info); | ||
648 | - updateSetting(info); | ||
649 | - } | ||
650 | - } else { | ||
651 | - for (int i = 0;; i++) { | ||
652 | - String data = prop.getProperty(String.valueOf(i)); | ||
653 | - if (data == null) { | ||
654 | - break; | ||
655 | - } | ||
656 | - ConnectionInfo info = new ConnectionInfo(data); | ||
657 | - settings.add(info); | ||
658 | - updateSetting(info); | ||
659 | - } | ||
660 | - } | ||
661 | - } else { | ||
662 | - settings.addAll(connInfoMap.values()); | ||
663 | - } | ||
664 | - Collections.sort(settings); | ||
665 | - return settings; | ||
666 | - } | ||
667 | - | ||
668 | - /** | ||
669 | - * Save the settings to the properties file. | ||
670 | - * | ||
671 | - * @param prop null or the properties webPort, webAllowOthers, and webSSL | ||
672 | - */ | ||
673 | - synchronized void saveProperties(Properties prop) { | ||
674 | - try { | ||
675 | - if (prop == null) { | ||
676 | - Properties old = loadProperties(); | ||
677 | - prop = new SortedProperties(); | ||
678 | - prop.setProperty("webPort", | ||
679 | - "" + SortedProperties.getIntProperty(old, | ||
680 | - "webPort", port)); | ||
681 | - prop.setProperty("webAllowOthers", | ||
682 | - "" + SortedProperties.getBooleanProperty(old, | ||
683 | - "webAllowOthers", allowOthers)); | ||
684 | - prop.setProperty("webSSL", | ||
685 | - "" + SortedProperties.getBooleanProperty(old, | ||
686 | - "webSSL", ssl)); | ||
687 | - if (commandHistoryString != null) { | ||
688 | - prop.setProperty(COMMAND_HISTORY, commandHistoryString); | ||
689 | - } | ||
690 | - } | ||
691 | - ArrayList<ConnectionInfo> settings = getSettings(); | ||
692 | - int len = settings.size(); | ||
693 | - for (int i = 0; i < len; i++) { | ||
694 | - ConnectionInfo info = settings.get(i); | ||
695 | - if (info != null) { | ||
696 | - prop.setProperty(String.valueOf(len - i - 1), info.getString()); | ||
697 | - } | ||
698 | - } | ||
699 | - if (!"null".equals(serverPropertiesDir)) { | ||
700 | - OutputStream out = FileUtils.newOutputStream( | ||
701 | - serverPropertiesDir + "/" + Constants.SERVER_PROPERTIES_NAME, false); | ||
702 | - prop.store(out, "H2 Server Properties"); | ||
703 | - out.close(); | ||
704 | - } | ||
705 | - } catch (Exception e) { | ||
706 | - DbException.traceThrowable(e); | ||
707 | - } | ||
708 | - } | ||
709 | - | ||
710 | - /** | ||
711 | - * Open a database connection. | ||
712 | - * | ||
713 | - * @param driver the driver class name | ||
714 | - * @param databaseUrl the database URL | ||
715 | - * @param user the user name | ||
716 | - * @param password the password | ||
717 | - * @return the database connection | ||
718 | - */ | ||
719 | - Connection getConnection(String driver, String databaseUrl, String user, | ||
720 | - String password) throws SQLException { | ||
721 | - driver = driver.trim(); | ||
722 | - databaseUrl = databaseUrl.trim(); | ||
723 | - org.h2.Driver.load(); | ||
724 | - Properties p = new Properties(); | ||
725 | - p.setProperty("user", user.trim()); | ||
726 | - // do not trim the password, otherwise an | ||
727 | - // encrypted H2 database with empty user password doesn't work | ||
728 | - p.setProperty("password", password); | ||
729 | - if (databaseUrl.startsWith("jdbc:h2:")) { | ||
730 | - if (ifExists) { | ||
731 | - databaseUrl += ";IFEXISTS=TRUE"; | ||
732 | - } | ||
733 | - // PostgreSQL would throw a NullPointerException | ||
734 | - // if it is loaded before the H2 driver | ||
735 | - // because it can't deal with non-String objects in the connection | ||
736 | - // Properties | ||
737 | - return org.h2.Driver.load().connect(databaseUrl, p); | ||
738 | - } | ||
739 | -// try { | ||
740 | -// Driver dr = (Driver) urlClassLoader. | ||
741 | -// loadClass(driver).newInstance(); | ||
742 | -// return dr.connect(url, p); | ||
743 | -// } catch(ClassNotFoundException e2) { | ||
744 | -// throw e2; | ||
745 | -// } | ||
746 | - return JdbcUtils.getConnection(driver, databaseUrl, p); | ||
747 | - } | ||
748 | - | ||
749 | - /** | ||
750 | - * Shut down the web server. | ||
751 | - */ | ||
752 | - void shutdown() { | ||
753 | - if (shutdownHandler != null) { | ||
754 | - shutdownHandler.shutdown(); | ||
755 | - } | ||
756 | - } | ||
757 | - | ||
758 | - public void setShutdownHandler(ShutdownHandler shutdownHandler) { | ||
759 | - this.shutdownHandler = shutdownHandler; | ||
760 | - } | ||
761 | - | ||
762 | - /** | ||
763 | - * Create a session with a given connection. | ||
764 | - * | ||
765 | - * @param conn the connection | ||
766 | - * @return the URL of the web site to access this connection | ||
767 | - */ | ||
768 | - public String addSession(Connection conn) throws SQLException { | ||
769 | - WebSession session = createNewSession("local"); | ||
770 | - session.setShutdownServerOnDisconnect(); | ||
771 | - session.setConnection(conn); | ||
772 | - session.put("url", conn.getMetaData().getURL()); | ||
773 | - String s = (String) session.get("sessionId"); | ||
774 | - return url + "/frame.jsp?jsessionid=" + s; | ||
775 | - } | ||
776 | - | ||
777 | - /** | ||
778 | - * The translate thread reads and writes the file translation.properties | ||
779 | - * once a second. | ||
780 | - */ | ||
781 | - private class TranslateThread extends Thread { | ||
782 | - | ||
783 | - private final File file = new File("translation.properties"); | ||
784 | - private final Map<Object, Object> translation; | ||
785 | - private volatile boolean stopNow; | ||
786 | - | ||
787 | - TranslateThread(Map<Object, Object> translation) { | ||
788 | - this.translation = translation; | ||
789 | - } | ||
790 | - | ||
791 | - public String getFileName() { | ||
792 | - return file.getAbsolutePath(); | ||
793 | - } | ||
794 | - | ||
795 | - public void stopNow() { | ||
796 | - this.stopNow = true; | ||
797 | - try { | ||
798 | - join(); | ||
799 | - } catch (InterruptedException e) { | ||
800 | - // ignore | ||
801 | - } | ||
802 | - } | ||
803 | - | ||
804 | - @Override | ||
805 | - public void run() { | ||
806 | - while (!stopNow) { | ||
807 | - try { | ||
808 | - SortedProperties sp = new SortedProperties(); | ||
809 | - if (file.exists()) { | ||
810 | - InputStream in = FileUtils.newInputStream(file.getName()); | ||
811 | - sp.load(in); | ||
812 | - translation.putAll(sp); | ||
813 | - } else { | ||
814 | - OutputStream out = FileUtils.newOutputStream(file.getName(), false); | ||
815 | - sp.putAll(translation); | ||
816 | - sp.store(out, "Translation"); | ||
817 | - } | ||
818 | - Thread.sleep(1000); | ||
819 | - } catch (Exception e) { | ||
820 | - traceError(e); | ||
821 | - } | ||
822 | - } | ||
823 | - } | ||
824 | - | ||
825 | - } | ||
826 | - | ||
827 | - /** | ||
828 | - * Start the translation thread that reads the file once a second. | ||
829 | - * | ||
830 | - * @param translation the translation map | ||
831 | - * @return the name of the file to translate | ||
832 | - */ | ||
833 | - String startTranslate(Map<Object, Object> translation) { | ||
834 | - if (translateThread != null) { | ||
835 | - translateThread.stopNow(); | ||
836 | - } | ||
837 | - translateThread = new TranslateThread(translation); | ||
838 | - translateThread.setDaemon(true); | ||
839 | - translateThread.start(); | ||
840 | - return translateThread.getFileName(); | ||
841 | - } | ||
842 | - | ||
843 | - @Override | ||
844 | - public boolean isDaemon() { | ||
845 | - return isDaemon; | ||
846 | - } | ||
847 | - | ||
848 | - void setAllowChunked(boolean allowChunked) { | ||
849 | - this.allowChunked = allowChunked; | ||
850 | - } | ||
851 | - | ||
852 | - boolean getAllowChunked() { | ||
853 | - return allowChunked; | ||
854 | - } | ||
855 | - | ||
856 | -} | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
1 | -package org.legrog.util; | ||
2 | -// Code copied from org.h2.server.web.WebSession | ||
3 | -/* | ||
4 | - * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0, | ||
5 | - * and the EPL 1.0 (http://h2database.com/html/license.html). | ||
6 | - * Initial Developer: H2 Group | ||
7 | - */ | ||
8 | -///package org.h2.server.web; | ||
9 | - | ||
10 | - import java.sql.Connection; | ||
11 | - import java.sql.DatabaseMetaData; | ||
12 | - import java.sql.ResultSet; | ||
13 | - import java.sql.SQLException; | ||
14 | - import java.sql.Statement; | ||
15 | - import java.sql.Timestamp; | ||
16 | - import java.util.ArrayList; | ||
17 | - import java.util.HashMap; | ||
18 | - import java.util.Locale; | ||
19 | - | ||
20 | - import org.h2.bnf.Bnf; | ||
21 | - import org.h2.bnf.context.DbContents; | ||
22 | - import org.h2.bnf.context.DbContextRule; | ||
23 | - import org.h2.message.DbException; | ||
24 | - import org.h2.util.New; | ||
25 | - | ||
26 | -/** | ||
27 | - * The web session keeps all data of a user session. | ||
28 | - * This class is used by the H2 Console. | ||
29 | - */ | ||
30 | -class WebSession { | ||
31 | - | ||
32 | - private static final int MAX_HISTORY = 1000; | ||
33 | - | ||
34 | - /** | ||
35 | - * The last time this client sent a request. | ||
36 | - */ | ||
37 | - long lastAccess; | ||
38 | - | ||
39 | - /** | ||
40 | - * The session attribute map. | ||
41 | - */ | ||
42 | - final HashMap<String, Object> map = New.hashMap(); | ||
43 | - | ||
44 | - /** | ||
45 | - * The current locale. | ||
46 | - */ | ||
47 | - Locale locale; | ||
48 | - | ||
49 | - /** | ||
50 | - * The currently executing statement. | ||
51 | - */ | ||
52 | - Statement executingStatement; | ||
53 | - | ||
54 | - /** | ||
55 | - * The current updatable result set. | ||
56 | - */ | ||
57 | - ResultSet result; | ||
58 | - | ||
59 | - private final WebServer server; | ||
60 | - | ||
61 | - private final ArrayList<String> commandHistory; | ||
62 | - | ||
63 | - private Connection conn; | ||
64 | - private DatabaseMetaData meta; | ||
65 | - private DbContents contents = new DbContents(); | ||
66 | - private Bnf bnf; | ||
67 | - private boolean shutdownServerOnDisconnect; | ||
68 | - | ||
69 | - WebSession(WebServer server) { | ||
70 | - this.server = server; | ||
71 | - // This must be stored in the session rather than in the server. | ||
72 | - // Otherwise, one client could allow | ||
73 | - // saving history for others (insecure). | ||
74 | - this.commandHistory = server.getCommandHistoryList(); | ||
75 | - } | ||
76 | - | ||
77 | - /** | ||
78 | - * Put an attribute value in the map. | ||
79 | - * | ||
80 | - * @param key the key | ||
81 | - * @param value the new value | ||
82 | - */ | ||
83 | - void put(String key, Object value) { | ||
84 | - map.put(key, value); | ||
85 | - } | ||
86 | - | ||
87 | - /** | ||
88 | - * Get the value for the given key. | ||
89 | - * | ||
90 | - * @param key the key | ||
91 | - * @return the value | ||
92 | - */ | ||
93 | - Object get(String key) { | ||
94 | - if ("sessions".equals(key)) { | ||
95 | - return server.getSessions(); | ||
96 | - } | ||
97 | - return map.get(key); | ||
98 | - } | ||
99 | - | ||
100 | - /** | ||
101 | - * Remove a session attribute from the map. | ||
102 | - * | ||
103 | - * @param key the key | ||
104 | - */ | ||
105 | - void remove(String key) { | ||
106 | - map.remove(key); | ||
107 | - } | ||
108 | - | ||
109 | - /** | ||
110 | - * Get the BNF object. | ||
111 | - * | ||
112 | - * @return the BNF object | ||
113 | - */ | ||
114 | - Bnf getBnf() { | ||
115 | - return bnf; | ||
116 | - } | ||
117 | - | ||
118 | - /** | ||
119 | - * Load the SQL grammar BNF. | ||
120 | - */ | ||
121 | - void loadBnf() { | ||
122 | - try { | ||
123 | - Bnf newBnf = Bnf.getInstance(null); | ||
124 | - DbContextRule columnRule = | ||
125 | - new DbContextRule(contents, DbContextRule.COLUMN); | ||
126 | - DbContextRule newAliasRule = | ||
127 | - new DbContextRule(contents, DbContextRule.NEW_TABLE_ALIAS); | ||
128 | - DbContextRule aliasRule = | ||
129 | - new DbContextRule(contents, DbContextRule.TABLE_ALIAS); | ||
130 | - DbContextRule tableRule = | ||
131 | - new DbContextRule(contents, DbContextRule.TABLE); | ||
132 | - DbContextRule schemaRule = | ||
133 | - new DbContextRule(contents, DbContextRule.SCHEMA); | ||
134 | - DbContextRule columnAliasRule = | ||
135 | - new DbContextRule(contents, DbContextRule.COLUMN_ALIAS); | ||
136 | - DbContextRule procedure = | ||
137 | - new DbContextRule(contents, DbContextRule.PROCEDURE); | ||
138 | - newBnf.updateTopic("procedure", procedure); | ||
139 | - newBnf.updateTopic("column_name", columnRule); | ||
140 | - newBnf.updateTopic("new_table_alias", newAliasRule); | ||
141 | - newBnf.updateTopic("table_alias", aliasRule); | ||
142 | - newBnf.updateTopic("column_alias", columnAliasRule); | ||
143 | - newBnf.updateTopic("table_name", tableRule); | ||
144 | - newBnf.updateTopic("schema_name", schemaRule); | ||
145 | - newBnf.linkStatements(); | ||
146 | - bnf = newBnf; | ||
147 | - } catch (Exception e) { | ||
148 | - // ok we don't have the bnf | ||
149 | - server.traceError(e); | ||
150 | - } | ||
151 | - } | ||
152 | - | ||
153 | - /** | ||
154 | - * Get the SQL statement from history. | ||
155 | - * | ||
156 | - * @param id the history id | ||
157 | - * @return the SQL statement | ||
158 | - */ | ||
159 | - String getCommand(int id) { | ||
160 | - return commandHistory.get(id); | ||
161 | - } | ||
162 | - | ||
163 | - /** | ||
164 | - * Add a SQL statement to the history. | ||
165 | - * | ||
166 | - * @param sql the SQL statement | ||
167 | - */ | ||
168 | - void addCommand(String sql) { | ||
169 | - if (sql == null) { | ||
170 | - return; | ||
171 | - } | ||
172 | - sql = sql.trim(); | ||
173 | - if (sql.length() == 0) { | ||
174 | - return; | ||
175 | - } | ||
176 | - if (commandHistory.size() > MAX_HISTORY) { | ||
177 | - commandHistory.remove(0); | ||
178 | - } | ||
179 | - int idx = commandHistory.indexOf(sql); | ||
180 | - if (idx >= 0) { | ||
181 | - commandHistory.remove(idx); | ||
182 | - } | ||
183 | - commandHistory.add(sql); | ||
184 | - if (server.isCommandHistoryAllowed()) { | ||
185 | - server.saveCommandHistoryList(commandHistory); | ||
186 | - } | ||
187 | - } | ||
188 | - | ||
189 | - /** | ||
190 | - * Get the list of SQL statements in the history. | ||
191 | - * | ||
192 | - * @return the commands | ||
193 | - */ | ||
194 | - ArrayList<String> getCommandHistory() { | ||
195 | - return commandHistory; | ||
196 | - } | ||
197 | - | ||
198 | - /** | ||
199 | - * Update session meta data information and get the information in a map. | ||
200 | - * | ||
201 | - * @return a map containing the session meta data | ||
202 | - */ | ||
203 | - HashMap<String, Object> getInfo() { | ||
204 | - HashMap<String, Object> m = New.hashMap(); | ||
205 | - m.putAll(map); | ||
206 | - m.put("lastAccess", new Timestamp(lastAccess).toString()); | ||
207 | - try { | ||
208 | - m.put("url", conn == null ? | ||
209 | - "${text.admin.notConnected}" : conn.getMetaData().getURL()); | ||
210 | - m.put("user", conn == null ? | ||
211 | - "-" : conn.getMetaData().getUserName()); | ||
212 | - m.put("lastQuery", commandHistory.size() == 0 ? | ||
213 | - "" : commandHistory.get(0)); | ||
214 | - m.put("executing", executingStatement == null ? | ||
215 | - "${text.admin.no}" : "${text.admin.yes}"); | ||
216 | - } catch (SQLException e) { | ||
217 | - DbException.traceThrowable(e); | ||
218 | - } | ||
219 | - return m; | ||
220 | - } | ||
221 | - | ||
222 | - void setConnection(Connection conn) throws SQLException { | ||
223 | - this.conn = conn; | ||
224 | - if (conn == null) { | ||
225 | - meta = null; | ||
226 | - } else { | ||
227 | - meta = conn.getMetaData(); | ||
228 | - } | ||
229 | - contents = new DbContents(); | ||
230 | - } | ||
231 | - | ||
232 | - DatabaseMetaData getMetaData() { | ||
233 | - return meta; | ||
234 | - } | ||
235 | - | ||
236 | - Connection getConnection() { | ||
237 | - return conn; | ||
238 | - } | ||
239 | - | ||
240 | - DbContents getContents() { | ||
241 | - return contents; | ||
242 | - } | ||
243 | - | ||
244 | - /** | ||
245 | - * Shutdown the server when disconnecting. | ||
246 | - */ | ||
247 | - void setShutdownServerOnDisconnect() { | ||
248 | - this.shutdownServerOnDisconnect = true; | ||
249 | - } | ||
250 | - | ||
251 | - boolean getShutdownServerOnDisconnect() { | ||
252 | - return shutdownServerOnDisconnect; | ||
253 | - } | ||
254 | - | ||
255 | - /** | ||
256 | - * Close the connection and stop the statement if one is currently | ||
257 | - * executing. | ||
258 | - */ | ||
259 | - void close() { | ||
260 | - if (executingStatement != null) { | ||
261 | - try { | ||
262 | - executingStatement.cancel(); | ||
263 | - } catch (Exception e) { | ||
264 | - // ignore | ||
265 | - } | ||
266 | - } | ||
267 | - if (conn != null) { | ||
268 | - try { | ||
269 | - conn.close(); | ||
270 | - } catch (Exception e) { | ||
271 | - // ignore | ||
272 | - } | ||
273 | - } | ||
274 | - | ||
275 | - } | ||
276 | - | ||
277 | -} | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
1 | -package org.legrog.util; | ||
2 | -// Code copied from org.h2.server.web.WebThread | ||
3 | -/* | ||
4 | - * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0, | ||
5 | - * and the EPL 1.0 (http://h2database.com/html/license.html). | ||
6 | - * Initial Developer: H2 Group | ||
7 | - */ | ||
8 | -//package org.h2.server.web; | ||
9 | - | ||
10 | - import java.io.BufferedInputStream; | ||
11 | - import java.io.BufferedOutputStream; | ||
12 | - import java.io.IOException; | ||
13 | - import java.io.InputStream; | ||
14 | - import java.io.OutputStream; | ||
15 | - import java.net.Socket; | ||
16 | - import java.net.UnknownHostException; | ||
17 | - import java.util.Iterator; | ||
18 | - import java.util.Locale; | ||
19 | - import java.util.Properties; | ||
20 | - import java.util.StringTokenizer; | ||
21 | - import org.h2.engine.Constants; | ||
22 | - import org.h2.engine.SysProperties; | ||
23 | - import org.h2.message.DbException; | ||
24 | - import org.h2.mvstore.DataUtils; | ||
25 | - import org.h2.util.IOUtils; | ||
26 | - import org.h2.util.NetUtils; | ||
27 | - import org.h2.util.StringUtils; | ||
28 | -//import org.h2.server.web.*; | ||
29 | - | ||
30 | -/** | ||
31 | - * For each connection to a session, an object of this class is created. | ||
32 | - * This class is used by the H2 Console. | ||
33 | - */ | ||
34 | -class WebThread extends WebApp implements Runnable { | ||
35 | - | ||
36 | - protected OutputStream output; | ||
37 | - protected final Socket socket; | ||
38 | - private final Thread thread; | ||
39 | - private InputStream input; | ||
40 | - private String ifModifiedSince; | ||
41 | - | ||
42 | - WebThread(Socket socket, WebServer server) { | ||
43 | - super(server); | ||
44 | - this.socket = socket; | ||
45 | - thread = new Thread(this, "H2 Console thread"); | ||
46 | - } | ||
47 | - | ||
48 | - /** | ||
49 | - * Start the thread. | ||
50 | - */ | ||
51 | - void start() { | ||
52 | - thread.start(); | ||
53 | - } | ||
54 | - | ||
55 | - /** | ||
56 | - * Wait until the thread is stopped. | ||
57 | - * | ||
58 | - * @param millis the maximum number of milliseconds to wait | ||
59 | - */ | ||
60 | - void join(int millis) throws InterruptedException { | ||
61 | - thread.join(millis); | ||
62 | - } | ||
63 | - | ||
64 | - /** | ||
65 | - * Close the connection now. | ||
66 | - */ | ||
67 | - void stopNow() { | ||
68 | - this.stop = true; | ||
69 | - try { | ||
70 | - socket.close(); | ||
71 | - } catch (IOException e) { | ||
72 | - // ignore | ||
73 | - } | ||
74 | - } | ||
75 | - | ||
76 | - private String getAllowedFile(String requestedFile) { | ||
77 | - if (!allow()) { | ||
78 | - return "notAllowed.jsp"; | ||
79 | - } | ||
80 | - if (requestedFile.length() == 0) { | ||
81 | - return "index.do"; | ||
82 | - } | ||
83 | - return requestedFile; | ||
84 | - } | ||
85 | - | ||
86 | - @Override | ||
87 | - public void run() { | ||
88 | - try { | ||
89 | - input = new BufferedInputStream(socket.getInputStream()); | ||
90 | - output = new BufferedOutputStream(socket.getOutputStream()); | ||
91 | - while (!stop) { | ||
92 | - if (!process()) { | ||
93 | - break; | ||
94 | - } | ||
95 | - } | ||
96 | - } catch (Exception e) { | ||
97 | - DbException.traceThrowable(e); | ||
98 | - } | ||
99 | - IOUtils.closeSilently(output); | ||
100 | - IOUtils.closeSilently(input); | ||
101 | - try { | ||
102 | - socket.close(); | ||
103 | - } catch (IOException e) { | ||
104 | - // ignore | ||
105 | - } finally { | ||
106 | - server.remove(this); | ||
107 | - } | ||
108 | - } | ||
109 | - | ||
110 | - @SuppressWarnings("unchecked") | ||
111 | - private boolean process() throws IOException { | ||
112 | - boolean keepAlive = false; | ||
113 | - String head = readHeaderLine(); | ||
114 | - if (head.startsWith("GET ") || head.startsWith("POST ")) { | ||
115 | - int begin = head.indexOf('/'), end = head.lastIndexOf(' '); | ||
116 | - String file; | ||
117 | - if (begin < 0 || end < begin) { | ||
118 | - file = ""; | ||
119 | - } else { | ||
120 | - file = head.substring(begin + 1, end).trim(); | ||
121 | - } | ||
122 | - trace(head + ": " + file); | ||
123 | - file = getAllowedFile(file); | ||
124 | - attributes = new Properties(); | ||
125 | - int paramIndex = file.indexOf("?"); | ||
126 | - session = null; | ||
127 | - if (paramIndex >= 0) { | ||
128 | - String attrib = file.substring(paramIndex + 1); | ||
129 | - parseAttributes(attrib); | ||
130 | - String sessionId = attributes.getProperty("jsessionid"); | ||
131 | - file = file.substring(0, paramIndex); | ||
132 | - session = server.getSession(sessionId); | ||
133 | - } | ||
134 | - keepAlive = parseHeader(); | ||
135 | - String hostAddr = socket.getInetAddress().getHostAddress(); | ||
136 | - file = processRequest(file, hostAddr); | ||
137 | - if (file.length() == 0) { | ||
138 | - // asynchronous request | ||
139 | - return true; | ||
140 | - } | ||
141 | - String message; | ||
142 | - byte[] bytes; | ||
143 | - if (cache && ifModifiedSince != null && | ||
144 | - ifModifiedSince.equals(server.getStartDateTime())) { | ||
145 | - bytes = null; | ||
146 | - message = "HTTP/1.1 304 Not Modified\r\n"; | ||
147 | - } else { | ||
148 | - bytes = server.getFile(file); | ||
149 | - if (bytes == null) { | ||
150 | - message = "HTTP/1.1 404 Not Found\r\n"; | ||
151 | - bytes = ("File not found: " + file).getBytes(Constants.UTF8); | ||
152 | - message += "Content-Length: " + bytes.length + "\r\n"; | ||
153 | - } else { | ||
154 | - if (session != null && file.endsWith(".jsp")) { | ||
155 | - String page = new String(bytes, Constants.UTF8); | ||
156 | - if (SysProperties.CONSOLE_STREAM) { | ||
157 | - Iterator<String> it = (Iterator<String>) session.map.remove("chunks"); | ||
158 | - if (it != null) { | ||
159 | - message = "HTTP/1.1 200 OK\r\n"; | ||
160 | - message += "Content-Type: " + mimeType + "\r\n"; | ||
161 | - message += "Cache-Control: no-cache\r\n"; | ||
162 | - message += "Transfer-Encoding: chunked\r\n"; | ||
163 | - message += "\r\n"; | ||
164 | - trace(message); | ||
165 | - output.write(message.getBytes()); | ||
166 | - while (it.hasNext()) { | ||
167 | - String s = it.next(); | ||
168 | - s = PageParser.parse(s, session.map); | ||
169 | - bytes = s.getBytes(Constants.UTF8); | ||
170 | - if (bytes.length == 0) { | ||
171 | - continue; | ||
172 | - } | ||
173 | - output.write(Integer.toHexString(bytes.length).getBytes()); | ||
174 | - output.write("\r\n".getBytes()); | ||
175 | - output.write(bytes); | ||
176 | - output.write("\r\n".getBytes()); | ||
177 | - output.flush(); | ||
178 | - } | ||
179 | - output.write("0\r\n\r\n".getBytes()); | ||
180 | - output.flush(); | ||
181 | - return keepAlive; | ||
182 | - } | ||
183 | - } | ||
184 | - page = PageParser.parse(page, session.map); | ||
185 | - bytes = page.getBytes(Constants.UTF8); | ||
186 | - } | ||
187 | - message = "HTTP/1.1 200 OK\r\n"; | ||
188 | - message += "Content-Type: " + mimeType + "\r\n"; | ||
189 | - if (!cache) { | ||
190 | - message += "Cache-Control: no-cache\r\n"; | ||
191 | - } else { | ||
192 | - message += "Cache-Control: max-age=10\r\n"; | ||
193 | - message += "Last-Modified: " + server.getStartDateTime() + "\r\n"; | ||
194 | - } | ||
195 | - message += "Content-Length: " + bytes.length + "\r\n"; | ||
196 | - } | ||
197 | - } | ||
198 | - message += "\r\n"; | ||
199 | - trace(message); | ||
200 | - output.write(message.getBytes()); | ||
201 | - if (bytes != null) { | ||
202 | - output.write(bytes); | ||
203 | - } | ||
204 | - output.flush(); | ||
205 | - } | ||
206 | - return keepAlive; | ||
207 | - } | ||
208 | - | ||
209 | - private String readHeaderLine() throws IOException { | ||
210 | - StringBuilder buff = new StringBuilder(); | ||
211 | - while (true) { | ||
212 | - int c = input.read(); | ||
213 | - if (c == -1) { | ||
214 | - throw new IOException("Unexpected EOF"); | ||
215 | - } else if (c == '\r') { | ||
216 | - if (input.read() == '\n') { | ||
217 | - return buff.length() > 0 ? buff.toString() : null; | ||
218 | - } | ||
219 | - } else if (c == '\n') { | ||
220 | - return buff.length() > 0 ? buff.toString() : null; | ||
221 | - } else { | ||
222 | - buff.append((char) c); | ||
223 | - } | ||
224 | - } | ||
225 | - } | ||
226 | - | ||
227 | - private void parseAttributes(String s) { | ||
228 | - trace("data=" + s); | ||
229 | - while (s != null) { | ||
230 | - int idx = s.indexOf('='); | ||
231 | - if (idx >= 0) { | ||
232 | - String property = s.substring(0, idx); | ||
233 | - s = s.substring(idx + 1); | ||
234 | - idx = s.indexOf('&'); | ||
235 | - String value; | ||
236 | - if (idx >= 0) { | ||
237 | - value = s.substring(0, idx); | ||
238 | - s = s.substring(idx + 1); | ||
239 | - } else { | ||
240 | - value = s; | ||
241 | - } | ||
242 | - String attr = StringUtils.urlDecode(value); | ||
243 | - attributes.put(property, attr); | ||
244 | - } else { | ||
245 | - break; | ||
246 | - } | ||
247 | - } | ||
248 | - trace(attributes.toString()); | ||
249 | - } | ||
250 | - | ||
251 | - private boolean parseHeader() throws IOException { | ||
252 | - boolean keepAlive = false; | ||
253 | - trace("parseHeader"); | ||
254 | - int len = 0; | ||
255 | - ifModifiedSince = null; | ||
256 | - boolean multipart = false; | ||
257 | - while (true) { | ||
258 | - String line = readHeaderLine(); | ||
259 | - if (line == null) { | ||
260 | - break; | ||
261 | - } | ||
262 | - trace(" " + line); | ||
263 | - String lower = StringUtils.toLowerEnglish(line); | ||
264 | - if (lower.startsWith("if-modified-since")) { | ||
265 | - ifModifiedSince = getHeaderLineValue(line); | ||
266 | - } else if (lower.startsWith("connection")) { | ||
267 | - String conn = getHeaderLineValue(line); | ||
268 | - if ("keep-alive".equals(conn)) { | ||
269 | - keepAlive = true; | ||
270 | - } | ||
271 | - } else if (lower.startsWith("content-type")) { | ||
272 | - String type = getHeaderLineValue(line); | ||
273 | - if (type.startsWith("multipart/form-data")) { | ||
274 | - multipart = true; | ||
275 | - } | ||
276 | - } else if (lower.startsWith("content-length")) { | ||
277 | - len = Integer.parseInt(getHeaderLineValue(line)); | ||
278 | - trace("len=" + len); | ||
279 | - } else if (lower.startsWith("user-agent")) { | ||
280 | - boolean isWebKit = lower.contains("webkit/"); | ||
281 | - if (isWebKit && session != null) { | ||
282 | - // workaround for what seems to be a WebKit bug: | ||
283 | - // http://code.google.com/p/chromium/issues/detail?id=6402 | ||
284 | - session.put("frame-border", "1"); | ||
285 | - session.put("frameset-border", "2"); | ||
286 | - } | ||
287 | - } else if (lower.startsWith("accept-language")) { | ||
288 | - Locale locale = session == null ? null : session.locale; | ||
289 | - if (locale == null) { | ||
290 | - String languages = getHeaderLineValue(line); | ||
291 | - StringTokenizer tokenizer = new StringTokenizer(languages, ",;"); | ||
292 | - while (tokenizer.hasMoreTokens()) { | ||
293 | - String token = tokenizer.nextToken(); | ||
294 | - if (!token.startsWith("q=")) { | ||
295 | - if (server.supportsLanguage(token)) { | ||
296 | - int dash = token.indexOf('-'); | ||
297 | - if (dash >= 0) { | ||
298 | - String language = token.substring(0, dash); | ||
299 | - String country = token.substring(dash + 1); | ||
300 | - locale = new Locale(language, country); | ||
301 | - } else { | ||
302 | - locale = new Locale(token, ""); | ||
303 | - } | ||
304 | - headerLanguage = locale.getLanguage(); | ||
305 | - if (session != null) { | ||
306 | - session.locale = locale; | ||
307 | - session.put("language", headerLanguage); | ||
308 | - server.readTranslations(session, headerLanguage); | ||
309 | - } | ||
310 | - break; | ||
311 | - } | ||
312 | - } | ||
313 | - } | ||
314 | - } | ||
315 | - } else if (line.trim().length() == 0) { | ||
316 | - break; | ||
317 | - } | ||
318 | - } | ||
319 | - if (multipart) { | ||
320 | - // not supported | ||
321 | - } else if (session != null && len > 0) { | ||
322 | - byte[] bytes = DataUtils.newBytes(len); | ||
323 | - for (int pos = 0; pos < len;) { | ||
324 | - pos += input.read(bytes, pos, len - pos); | ||
325 | - } | ||
326 | - String s = new String(bytes); | ||
327 | - parseAttributes(s); | ||
328 | - } | ||
329 | - return keepAlive; | ||
330 | - } | ||
331 | - | ||
332 | - private static String getHeaderLineValue(String line) { | ||
333 | - return line.substring(line.indexOf(':') + 1).trim(); | ||
334 | - } | ||
335 | - | ||
336 | - @Override | ||
337 | - protected String adminShutdown() { | ||
338 | - stopNow(); | ||
339 | - return super.adminShutdown(); | ||
340 | - } | ||
341 | - | ||
342 | - private boolean allow() { | ||
343 | - if (server.getAllowOthers()) { | ||
344 | - return true; | ||
345 | - } | ||
346 | - try { | ||
347 | - return NetUtils.isLocalAddress(socket); | ||
348 | - } catch (UnknownHostException e) { | ||
349 | - server.traceError(e); | ||
350 | - return false; | ||
351 | - } | ||
352 | - } | ||
353 | - | ||
354 | - private void trace(String s) { | ||
355 | - server.trace(s); | ||
356 | - } | ||
357 | -} | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
-
Please register or login to post a comment