Существует простой способ настроить его так, чтобы он получал любой вывод, необходимый для преобразования JSP в любую нужную форму, включая объектную модель страницы:
- Определите подкласс Node.Visitor для обработки узлов (тегов и т. Д.) JSP.
- Написать простой подкласс Compiler, переопределяя его generateJava () для вызова посетителя.
- Подкласс исполнителя-компилятора JspC переопределяет его метод getCompilerClassName () для возврата вашего класса компилятора
Давайте посмотрим код.
Реализация
1. Пользовательский посетитель
Visitor вызывается компилятором для обработки объектной модели дерева проанализированного JSP. Эта реализация просто печатает информацию об интересующем подмножестве узлов на странице с отступом, чтобы сделать их вложение понятным.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
|
package org.apache.jasper.compiler; import java.util.LinkedList; import org.apache.jasper.JasperException; import org.apache.jasper.compiler.Node.CustomTag; import org.apache.jasper.compiler.Node.ELExpression; import org.apache.jasper.compiler.Node.IncludeDirective; import org.apache.jasper.compiler.Node.Visitor; import org.xml.sax.Attributes; public class JsfElCheckingVisitor extends Visitor { private String indent = "" ; @Override public void visit(ELExpression n) throws JasperException { logEntry( "ELExpression" , n, "EL: " + n.getEL()); super .visit(n); } @Override public void visit(IncludeDirective n) throws JasperException { logEntry( "IncludeDirective" , n, toString(n.getAttributes())); super .visit(n); } @Override public void visit(CustomTag n) throws JasperException { logEntry( "CustomTag" , n, "Class: " + n.getTagHandlerClass().getName() + ", attrs: " + toString(n.getAttributes())); doVisit(n); indent += " " ; visitBody(n); indent = indent.substring( 0 , indent.length() - 1 ); } private String toString(Attributes attributes) { if (attributes == null || attributes.getLength() == 0 ) return "" ; LinkedList<String> details = new LinkedList<String>(); for ( int i = 0 ; i < attributes.getLength(); i++) { details.add(attributes.getQName(i) + "=" + attributes.getValue(i)); } return details.toString(); } private void logEntry(String what, Node n, String details) { System.out.println(indent + n.getQName() + " at line:" + n.getStart().getLineNumber() + ": " + details); } } |
Заметки:
- Посетитель должен быть в пакете org.apache.jasper.compiler, поскольку необходимый класс org.apache.jasper.compiler.Node является закрытым для пакета.
- Метод visitBody запускает обработку вложенных узлов
- Есть и другие методы, которые я мог бы переопределить (и метод catch-all doVisit), но я выбрал только те, которые мне интересны
- Атрибуты узла имеют тип… саксофон. Атрибуты , которые содержат имена атрибутов и значения в виде строк
- attribute.getType (i) обычно является CDATA
- Структура Node содержит информацию о родительском узле, имени тега, классе обработчика тега, соответствующей строке исходного файла и имени исходного файла, а также другую полезную информацию.
- CustomTag , вероятно, наиболее интересный тип узла, например, все теги JSF относятся к этому типу.
Пример вывода (для страницы JSF)
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
jsp:directive.include at line:5: [ file =includes /stdjsp .jsp] jsp:directive.include at line:6: [ file =includes /ssoinclude .jsp] f:verbatim at line:14: Class: com.sun.faces.taglib.jsf_core.VerbatimTag, attrs: htm:div at line:62: Class: com.exadel.htmLib.tags.DivTag, attrs: [style=width:100%;] h:form at line:64: Class: com.sun.faces.taglib.html_basic.FormTag, attrs: [ id =inputForm] htm:table at line:66: Class: com.exadel.htmLib.tags.TableTag, attrs: [cellpadding=0, width=100%, border=0, styleClass= clear box_main] htm: tr at line:71: Class: com.exadel.htmLib.tags.TrTag, attrs: htm:td at line:72: Class: com.exadel.htmLib.tags.TdTag, attrs: f:subview at line:73: Class: com.sun.faces.taglib.jsf_core.SubviewTag, attrs: [ id =cars] jsp:directive.include at line:74: [ file = /includes/cars .jsp] h:panelGroup at line:8: Class: com.sun.faces.taglib.html_basic.PanelGroupTag, attrs: [rendered= #{bookingHandler.flowersAvailable}] ... htm: tr at line:87: Class: com.exadel.htmLib.tags.TrTag, attrs: [style=height:5px] htm:td at line:87: Class: com.exadel.htmLib.tags.TdTag, attrs: |
(Я не печатаю «закрывающие теги», поскольку ясно, что тег заканчивается, когда появляется другой узел с таким же или меньшим отступом или заканчивается вывод.)
2. Подкласс компилятора
Важной частью является generateJava, который я только что скопировал, удалил из него некоторый код и добавил вызов моего посетителя. Так что на самом деле только 3 строки в списке ниже являются новыми (6, 56, 70)
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
|
public class OnlyReadingJspPseudoCompiler extends Compiler { /** We're never compiling .java to .class. */ @Override protected void generateClass(String[] smap) throws FileNotFoundException, JasperException, Exception { return ; } /** Copied from {@link Compiler#generateJava()} and adjusted */ @Override protected String[] generateJava() throws Exception { // Setup page info area pageInfo = new PageInfo( new BeanRepository(ctxt.getClassLoader(), errDispatcher), ctxt.getJspFile()); // JH: Skipped processing of jsp-property-group in web.xml for the current page if (ctxt.isTagFile()) { try { double libraryVersion = Double.parseDouble(ctxt.getTagInfo() .getTagLibrary().getRequiredVersion()); if (libraryVersion < 2.0 ) { pageInfo.setIsELIgnored( "true" , null , errDispatcher, true ); } if (libraryVersion < 2.1 ) { pageInfo.setDeferredSyntaxAllowedAsLiteral( "true" , null , errDispatcher, true ); } } catch (NumberFormatException ex) { errDispatcher.jspError(ex); } } ctxt.checkOutputDir(); try { // Parse the file ParserController parserCtl = new ParserController(ctxt, this ); // Pass 1 - the directives Node.Nodes directives = parserCtl.parseDirectives(ctxt.getJspFile()); Validator.validateDirectives( this , directives); // Pass 2 - the whole translation unit pageNodes = parserCtl.parse(ctxt.getJspFile()); // Validate and process attributes - don't re-validate the // directives we validated in pass 1 /** * JH: The code above has been copied from Compiler#generateJava() with some * omissions and with using our own Visitor. * The code that used to follow was just deleted. * Note: The JSP's name is in ctxt.getJspFile() */ pageNodes.visit( new JsfElCheckingVisitor()); } finally {} return null ; } /** * The parent's implementation, in our case, checks whether the target file * exists and returns true if it doesn't. However it is expensive so * we skip it by returning true directly. * @see org.apache.jasper.JspCompilationContext#getServletJavaFileName() */ @Override public boolean isOutDated( boolean checkClass) { return true ; } } |
Заметки:
- Я удалил довольно много кода, неважного для меня, из генерации Java; для анализа другого типа, чем я предполагаю, часть этого кода могла бы быть полезной, поэтому посмотрите на оригинальный класс Compiler и решите сами.
- На самом деле меня не волнуют EL JSP, поэтому можно было бы оптимизировать компилятор так, чтобы ему потребовался только один проход.
3. Компилятор-исполнитель
Трудно использовать компилятор напрямую, потому что он зависит от множества сложных настроек и объектов. Таким образом, проще всего повторно использовать задачу Ant JspC, что дает дополнительное преимущество поиска JSP для обработки. Как уже упоминалось, ключевым моментом является переопределение getCompilerClassName для возврата класса моего компилятора (строка 8)
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
import org.apache.jasper.JspC; /** Extends JspC to use the compiler of our choice; Jasper version 6.0.29. */ public class JspCParsingToNodesOnly extends JspC { /** Overriden to return the class of ours (default = null => JdtCompiler) */ @Override public String getCompilerClassName() { return OnlyReadingJspPseudoCompiler. class .getName(); } public static void main(String[] args) { JspCParsingToNodesOnly jspc = new JspCParsingToNodesOnly(); jspc.setUriroot( "web" ); // where to search for JSPs //jspc.setVerbose(1); // 0 = false, 1 = true jspc.setJspFiles( "helloJSFpage.jsp" ); // leave unset to process all; comma-separated try { jspc.execute(); } catch (JasperException e) { throw new RuntimeException(e); } } } |
Заметки:
- JspC обычно находит все файлы в указанном Uriroot, но вы можете сказать ему, что он должен игнорировать все, кроме некоторых выбранных, передав их имена через запятую в setJspFiles.
Зависимости компиляции
В форме твоего плюща:
1
2
3
|
< dependency name = "jasper" org = "org.apache.tomcat" rev = "6.0.29" > < dependency name = "jasper-jdt" org = "org.apache.tomcat" rev = "6.0.29" > < dependency name = "ant" org = "org.apache.ant" rev = "1.8.2" > |
Лицензия
Весь код здесь напрямую получен из Jasper и, следовательно, подпадает под ту же лицензию, то есть лицензию Apache, версия 2.0 .
Вывод
Jasper на самом деле не был разработан для расширения и модульности, что подтверждается тем фактом, что критически важный класс Node является закрытым пакетом, а его API настолько сложен, что повторное использование только его части очень сложно. К счастью, задача Ant JspC делает ее пригодной для использования вне контейнера сервлета, предоставляя некоторые «поддельные» объекты, и есть способ настроить ее под наши нужды с минимальными затратами, хотя это было нелегко понять. Мне пришлось применить некоторые хитрые уловки, а именно использовать вещи из закрытого пакета и переопределить метод, который не предназначен для переопределения ( generateJava ), но он работает и обеспечивает очень ценный вывод, который позволяет делать все, что вы захотите делать с JSP.
- Java Code Geeks Andygene Web Archetype
- Почему автоматизированные тесты ускоряют вашу разработку
- Качество кода имеет значение для клиентов. Много.
- Использование FindBugs для создания значительно меньшего количества ошибочного кода
- Рекомендации по разработке гибкого программного обеспечения для пользователей и новых пользователей