/* * Copyright 2004-2011 the Seasar Foundation and the Others. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, * either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ package org.seasar.mayaa.impl.ex.rhino; import java.awt.BorderLayout; import java.awt.Font; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.atomic.AtomicBoolean; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JFrame; import javax.swing.JTextArea; import org.mozilla.javascript.Undefined; import org.seasar.mayaa.impl.cycle.script.ScriptUtil; /** * Mayaaの簡易デバッガ。 *

* * Packages.org.seasar.mayaa.impl.ex.rhino.MayaaDebugger.debug() * * と実行した位置でJavaScriptの入力ウィンドウが開き、executeボタンまたはCtrl+Enterで実行。 * 実行結果は{@link java.lang.System#out(java.lang.Object)}で出力される。 * 内部を変更するスクリプトなどもそのまま実行できる。 *

*

* このウィンドウが開いた状態ではTomcatが終了しないのでウィンドウをちゃんと閉じること。 *

*/ public class MayaaDebugger { private static final String EXIT_COMMAND = ""; private static final int DEFAULT_WIDTH = 400; private static final int DEFAULT_HEIGHT = 300; private static boolean isNotLocalhost(final String remoteAddr) { return (remoteAddr.equals("127.0.0.1") || remoteAddr.equals("0:0:0:0:0:0:0:1")) == false; } private static Object executeScript(final String script) { return ScriptUtil.compile(script, Object.class).execute(null); } /** * デバッグ */ public static void debug() { try { // REMOTE_ADDR が localhost でなければ何もしない Object remoteAddr = executeScript("${ request.underlyingContext.remoteAddr }"); if (isNotLocalhost(String.valueOf(remoteAddr))) { return; } // page.skipdebugger が true の場合は起動しない Object skipResult = executeScript("${ page['skipdebugger'] }"); if (skipResult != null && skipResult instanceof Boolean && ((Boolean) skipResult).booleanValue()) { return; } } catch (Throwable t) { t.printStackTrace(); return; } openDebugWindow(DEFAULT_WIDTH, DEFAULT_HEIGHT); } private static void openDebugWindow(final int width, final int height) { // 実行するスクリプトのキュー final BlockingQueue scriptQueue = new ArrayBlockingQueue(1); // 実行結果を格納するキュー (null不可なのでUndefinedを使う) final BlockingQueue resultQueue = new ArrayBlockingQueue(1); final AtomicBoolean dontShowAgain = new AtomicBoolean(false); String title = String.valueOf(executeScript("${ injectedNode.getSystemID() + ':' + injectedNode.getLineNumber() }")); // デバッグウィンドウ作成 final JFrame frame = new JFrame("debug"); frame.setTitle(title); frame.setLayout(new BorderLayout()); final JCheckBox pageCloseCheck = new JCheckBox("Don't show again on this page scope"); frame.getContentPane().add(pageCloseCheck, BorderLayout.NORTH); pageCloseCheck.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { dontShowAgain.set(pageCloseCheck.isSelected()); } }); final JTextArea textArea = new JTextArea(); textArea.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 16)); frame.getContentPane().add(textArea, BorderLayout.CENTER); final JButton execButton = new JButton("execute (CTRL+ENTER)"); execButton.addActionListener(new ActionListener() { @Override public void actionPerformed(final ActionEvent e) { try { // mayaaの実装をそのまま使うため${}で囲む。このため{@link #EXIT_COMMAND}を投げることはできない。 scriptQueue.put("${" + textArea.getText() + "}"); Object result = resultQueue.take(); if (result == Undefined.instance) { result = null; } // 結果オブジェクトをSystem.outに出力 System.out.println(result); } catch (Throwable t) { t.printStackTrace(); } } }); frame.getContentPane().add(execButton, BorderLayout.SOUTH); textArea.addKeyListener(new KeyListener() { @Override public void keyTyped(final KeyEvent e) { // do nothing. } @Override public void keyReleased(final KeyEvent e) { // do nothing. } @Override public void keyPressed(final KeyEvent e) { if (e.isControlDown() && e.getKeyCode() == KeyEvent.VK_ENTER) { execButton.doClick(); } } }); frame.addWindowListener(new WindowAdapter() { @Override public void windowOpened(final WindowEvent e) { // do nothing. } @Override public void windowClosing(final WindowEvent e) { // 閉じるときには空文字列をスクリプトキューに追加 try { if (dontShowAgain.get()) { scriptQueue.put("${ page['skipdebugger'] = true; }"); resultQueue.take(); } scriptQueue.put(EXIT_COMMAND); } catch (InterruptedException e1) { e1.printStackTrace(); } } }); frame.setSize(width, height); frame.setVisible(true); try { // スクリプトキューに空文字列が置かれるまでループ String text; while ((text = scriptQueue.take()).equals(EXIT_COMMAND) == false) { Object result = executeScript(text); if (result == null) { // resultQueueはnull不可 result = Undefined.instance; } resultQueue.put(result); } } catch (InterruptedException e1) { e1.printStackTrace(); } catch (Throwable t) { t.printStackTrace(); } finally { if (resultQueue.size() == 0) { resultQueue.add(Undefined.instance); } frame.dispose(); } } }