javax.swing.tree.TreeNode is a restricted class
問題
次の環境で、Google App EngineでStruts2を開発しています。
-
Struts 2.3.1.2
-
freemarker 2.3.18
-
JDK 1.6
-
Eclipse 3.7 + Eclipse用Googleプラグイン
-
Google App Engine Java SDK 1.6.3.1
javax.swing.tree.TreeNode
はローカル開発で制限されたクラスであるというGAEの苦情は、実際のGAE実稼働環境にデプロイされた場合、エラーメッセージは表示されなくなります。
Caused by: java.lang.NoClassDefFoundError: javax.swing.tree.TreeNode is a restricted class. Please see the Google App Engine developer's guide for more details. at com.google.appengine.tools.development.agent.runtime.Runtime.reject(Runtime.java:51) at freemarker.core.TextBlock.isIgnorable(TextBlock.java:375) at freemarker.core.TextBlock.heedsTrailingWhitespace(TextBlock.java:337)
溶液
GAE本番環境で機能する理由がわかりません。ローカルGAE環境では、TextBlock +`class, compile below code and move it to _WEB-INF/classes, so that it will overload the original `+TextBlock
クラスをオーバーロードできます。_
ファイル:TextBlock.java
/* * Copyright (c) 2003 The Visigoth Software Society. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, if * any, must include the following acknowledgement: * "This product includes software developed by the * Visigoth Software Society (http://www.visigoths.org/)." * Alternately, this acknowledgement may appear in the software itself, * if and wherever such third-party acknowledgements normally appear. * * 4. Neither the name "FreeMarker", "Visigoth", nor any of the names of the * project contributors may be used to endorse or promote products derived * from this software without prior written permission. For written * permission, please contact [email protected]. * * 5. Products derived from this software may not be called "FreeMarker" or "Visigoth" * nor may "FreeMarker" or "Visigoth" appear in their names * without prior written permission of the Visigoth Software Society. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE VISIGOTH SOFTWARE SOCIETY OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Visigoth Software Society. For more * information on the Visigoth Software Society, please see * http://www.visigoths.org/ */ package freemarker.core; import java.io.IOException; /** * A TemplateElement representing a block of plain text. * * @version $Id: TextBlock.java,v 1.17 2004/01/06 17:06:42 szegedia Exp $ */ public final class TextBlock extends TemplateElement { private static final char[] EMPTY_CHAR_ARRAY = new char[0]; static final TextBlock EMPTY_BLOCK = new TextBlock(EMPTY_CHAR_ARRAY, false); // We're using char[] instead of String for storing the text block because // Writer.write(String) involves copying the String contents to a char[] // using String.getChars(), and then calling Writer.write(char[]). By // using Writer.write(char[]) directly, we avoid array copying on each // write. private char[] text; private final boolean unparsed; public TextBlock(String text) { this(text, false); } public TextBlock(String text, boolean unparsed) { this(text.toCharArray(), unparsed); } private TextBlock(char[] text, boolean unparsed) { this.text = text; this.unparsed = unparsed; } /** * Simply outputs the text. */ public void accept(Environment env) throws IOException { env.getOut().write(text); } public String getCanonicalForm() { String text = new String(this.text); if (unparsed) { return "<#noparse>" + text + "#noparse>"; } return text; } public String getDescription() { String s = new String(text).trim(); if (s.length() == 0) { return "whitespace"; } if (s.length() > 20) { s = s.substring(0, 20) + "..."; s = s.replace('\n', ' '); s = s.replace('\r', ' '); } return "text block (" + s + ")"; } TemplateElement postParseCleanup(boolean stripWhitespace) { if (text.length == 0) return this; int openingCharsToStrip = 0, trailingCharsToStrip = 0; boolean deliberateLeftTrim = deliberateLeftTrim(); boolean deliberateRightTrim = deliberateRightTrim(); if (!stripWhitespace || text.length == 0) { return this; } if (parent.parent == null && previousSibling() == null) return this; if (!deliberateLeftTrim) { trailingCharsToStrip = trailingCharsToStrip(); } if (!deliberateRightTrim) { openingCharsToStrip = openingCharsToStrip(); } if (openingCharsToStrip == 0 && trailingCharsToStrip == 0) { return this; } this.text = substring(text, openingCharsToStrip, text.length - trailingCharsToStrip); if (openingCharsToStrip > 0) { this.beginLine++; this.beginColumn = 1; } if (trailingCharsToStrip > 0) { this.endColumn = 0; } return this; } /** * Scans forward the nodes on the same line to see whether there is a * deliberate left trim in effect. Returns true if the left trim was * present. */ private boolean deliberateLeftTrim() { boolean result = false; for (TemplateElement elem = this.nextTerminalNode(); elem != null && elem.beginLine == this.endLine; elem = elem .nextTerminalNode()) { if (elem instanceof TrimInstruction) { TrimInstruction ti = (TrimInstruction) elem; if (!ti.left && !ti.right) { result = true; } if (ti.left) { result = true; int lastNewLineIndex = lastNewLineIndex(); if (lastNewLineIndex >= 0 || beginColumn == 1) { char[] firstPart = substring(text, 0, lastNewLineIndex + 1); char[] lastLine = substring(text, 1 + lastNewLineIndex); if (trim(lastLine).length == 0) { this.text = firstPart; this.endColumn = 0; } else { int i = 0; while (Character.isWhitespace(lastLine[i])) { i++; } char[] printablePart = substring(lastLine, i); this.text = concat(firstPart, printablePart); } } } } } if (result) { } return result; } /** * Checks for the presence of a t or rt directive on the same line. Returns * true if the right trim directive was present. */ private boolean deliberateRightTrim() { boolean result = false; for (TemplateElement elem = this.prevTerminalNode(); elem != null && elem.endLine == this.beginLine; elem = elem .prevTerminalNode()) { if (elem instanceof TrimInstruction) { TrimInstruction ti = (TrimInstruction) elem; if (!ti.left && !ti.right) { result = true; } if (ti.right) { result = true; int firstLineIndex = firstNewLineIndex() + 1; if (firstLineIndex == 0) { return false; } if (text.length > firstLineIndex && text[firstLineIndex - 1] == '\r' && text[firstLineIndex] == '\n') { firstLineIndex++; } char[] trailingPart = substring(text, firstLineIndex); char[] openingPart = substring(text, 0, firstLineIndex); if (trim(openingPart).length == 0) { this.text = trailingPart; this.beginLine++; this.beginColumn = 1; } else { int lastNonWS = openingPart.length - 1; while (Character.isWhitespace(text[lastNonWS])) { lastNonWS--; } char[] printablePart = substring(text, 0, lastNonWS + 1); if (trim(trailingPart).length == 0) { // THIS BLOCK IS HEINOUS! THERE MUST BE A BETTER // WAY! REVISIT (JR) boolean trimTrailingPart = true; for (TemplateElement te = this.nextTerminalNode(); te != null && te.beginLine == this.endLine; te = te .nextTerminalNode()) { if (te.heedsOpeningWhitespace()) { trimTrailingPart = false; } if (te instanceof TrimInstruction && ((TrimInstruction) te).left) { trimTrailingPart = true; break; } } if (trimTrailingPart) trailingPart = EMPTY_CHAR_ARRAY; } this.text = concat(printablePart, trailingPart); } } } } return result; } /* * private String leftTrim(String s) { int i =0; while (i= 0 ? newlineIndex1 : newlineIndex2; if (newlineIndex1 >= 0 && newlineIndex2 >= 0) { result = Math.min(newlineIndex1, newlineIndex2); } return result; } private int lastNewLineIndex() { String content = new String(text); return Math.max(content.lastIndexOf('\r'), content.lastIndexOf('\n')); } /** * figures out how many opening whitespace characters to strip in the * post-parse cleanup phase. */ private int openingCharsToStrip() { int newlineIndex = firstNewLineIndex(); if (newlineIndex == -1 && beginColumn != 1) { return 0; } ++newlineIndex; if (text.length > newlineIndex) { if (newlineIndex > 0 && text[newlineIndex - 1] == '\r' && text[newlineIndex] == '\n') { ++newlineIndex; } } if (new String(text).substring(0, newlineIndex).trim().length() > 0) { return 0; } // We look at the preceding elements on the line to see if we should // strip the opening newline and any whitespace preceding it. for (TemplateElement elem = this.prevTerminalNode(); elem != null && elem.endLine == this.beginLine; elem = elem .prevTerminalNode()) { if (elem.heedsOpeningWhitespace()) { return 0; } } return newlineIndex; } /** * figures out how many trailing whitespace characters to strip in the * post-parse cleanup phase. */ private int trailingCharsToStrip() { String content = new String(text); int lastNewlineIndex = lastNewLineIndex(); if (lastNewlineIndex == -1 && beginColumn != 1) { return 0; } String substring = content.substring(lastNewlineIndex +1); if (substring.trim().length() >0) { return 0; } // We look at the elements afterward on the same line to see if we should // strip any whitespace after the last newline for (TemplateElement elem = this.nextTerminalNode(); elem != null && elem.beginLine == this.endLine; elem = elem.nextTerminalNode()) { if (elem.heedsTrailingWhitespace()) { return 0; } } return substring.length(); } boolean heedsTrailingWhitespace() { if (isIgnorable()) { return false; } for (int i = 0; i < text.length; i++) { char c = text[i]; if (c == '\n' || c == '\r') { return false; } if (!Character.isWhitespace(c)) { return true; } } return true; } boolean heedsOpeningWhitespace() { if (isIgnorable()) { return false; } for (int i = text.length - 1; i >= 0; i--) { char c = text[i]; if (c == '\n' || c == '\r') { return false; } if (!Character.isWhitespace(c)) { return true; } } return true; } boolean isIgnorable() { if (text == null || text.length == 0) { return true; } if (!isWhitespace()) { return false; } // do the trick boolean atTopLevel = true; TemplateElement prevSibling = previousSibling(); TemplateElement nextSibling = nextSibling(); return ((prevSibling == null && atTopLevel) || nonOutputtingType(prevSibling)) && ((nextSibling == null && atTopLevel) || nonOutputtingType(nextSibling)); } private boolean nonOutputtingType(TemplateElement element) { return (element instanceof Macro || element instanceof Assignment || element instanceof AssignmentInstruction || element instanceof PropertySetting || element instanceof LibraryLoad || element instanceof Comment); } private static char[] substring(char[] c, int from, int to) { char[] c2 = new char[to - from]; System.arraycopy(c, from, c2, 0, c2.length); return c2; } private static char[] substring(char[] c, int from) { return substring(c, from, c.length); } private static char[] trim(char[] c) { if (c.length == 0) { return c; } return new String(c).trim().toCharArray(); } private static char[] concat(char[] c1, char[] c2) { char[] c = new char[c1.length + c2.length]; System.arraycopy(c1, 0, c, 0, c1.length); System.arraycopy(c2, 0, c, c1.length, c2.length); return c; } boolean isWhitespace() { return text == null || trim(text).length == 0; } }