Создание динамических приложений с помощью javax.tools

Изучение и применение класса javax.tools.JavaCompiler для построения динамических приложений

Многие современные приложения требуют динамических возможностей, таких как возможность для пользователей задавать в абстрактной форме вычисления, расширяющие статические возможности приложения. Пакет javax.tools, добавленный в платформу Java™ Standard Edition 6 (Java SE) как стандартный API для компиляции исходного кода Java – это отличный способ достичь этой цели. В этой статье представлен обзор основных классов пакета и показано, как их использовать для создания фасада для компиляции исходного кода Java из Java-объектов String вместо файлов, а затем использовать этот фасад для создания интерактивного приложения для построения графиков.

Дэвид. Дж. Биесак, главный системный разработчик, IBM  

фото Девида Дж. БиесакДэвид. Дж. Биесак (David Biesack) работает главным системным разработчиком в лаборатории передовых вычислительных методов (Advanced Computing Lab) компании SAS Institute, Inc., где занимается сложной аналитикой и распределенными вычислениями. Дэвид проектирует и пишет программы на языке Java в течение 12 лет из 19, которые он провел в SAS. Он участвовал в JSR 201, в ходе которого в Java 5 были добавлены новые синтаксические возможности.



16.11.2009

Введение

Пакет javax.tools, добавленный в Java SE 6 как стандартный API для компиляции исходного кода Java, позволяет добавлять динамическую функциональность для расширения статических приложений. Эта статья представляет обзор основных классов пакета и показывает, как использовать их для компиляции исходного кода Java из Java-объектов String, StringBuffer или CharSequence вместо файлов. Затем этот фасад используется для построения интерактивного приложения для построения графиков, которое позволяет пользователю задать числовую функцию y = f(x) с помощью любого правильного числового Java-выражения. В конце обсуждаются возможные угрозы безопасности, связанные с динамической компиляцией исходного кода, и способы для устранения этих рисков.

Идея расширения приложений через компиляцию и загрузку Java-расширений не нова, и существует несколько каркасов, поддерживающих такую функциональность. Технология серверных Java-страниц (JavaServer Pages - JSP) в платформе Java Enterprise Edition (Java EE) – это широко известный пример динамического каркаса, которые генерирует и компилирует Java-классы. Транслятор JSP трансформирует .jsp файлы в Java-сервлеты (servlets), используя промежуточные файлы исходного кода, которые затем компилируются JSP-процессором и загружаются в Java EE-контейнер Java-сервлетов. Компиляция часто выполняется прямым вызовом компилятора javac, который требует установленного комплекта Java-разработчика (Java Development Kit - JDK), или вызовом класса com.sun.tools.javac.Main, который можно найти в JAR-архиве tools.jar от Sun. Лицензия Sun позволяет распространять файл tools.jar с полной версией среды исполнения Java (Java Runtime Environment - JRE). Другие способы реализовать подобные динамические возможности включают в себя использование языков динамических сценариев (таких как JavaScript или Groovy), которые интегрируются с языком реализации приложения (см. раздел Ресурсы) или написание специального доменно-ориентированного языка и связанного с ним интерпретатора или компилятора.

Другие среды (такие как NetBeans и Eclipse) допускают расширения, написанные непосредственно на языке Java, но подобные системы требуют внешней статической компиляции и управления исходным и бинарным Java-кодом и его артефактами. Технология Apache Commons JCI предоставляет механизм для компиляции и загрузки Java-классов в работающее приложение. Технологии Janino и Javassist также предоставляют сходные динамические возможности, хотя Janino ограничена конструкциями языка до версии Java 1.4, а Javassist работает не на уровне исходного кода, а на уровне абстракции Java-классов. В разделе Ресурсы приведены ссылки на эти проекты. Однако, поскольку Java-разработчики уже привыкли писать на языке Java, система, которая позволяет просто генерировать исходный код Java на лету, а затем компилировать и загружать, обещает самый простой путь обучения и максимальную гибкость.

Преимущества использования javax.tools

Использование javax.tools предоставляет следующие преимущества:

  • Это одобренное расширение платформы Java SE, а, значит, это стандартный API, разработанный в рамках Java Community Process (JSR 199). API-интерфейс com.sun.tools.javac.Main по спецификации не является частью документированного API платформы Java и необязательно имеется в JDK от других поставщиков; также не гарантировано наличие этого PI в будущих версиях Sun JDK.
  • При этом вы используете то, что знаете: исходный код Java, а не байт-код. Можно создавать правильные Java-классы, генерируя соответствующий исходный код Java, без изучения более сложных правил правильного байт-кода или новой объектной модели классов, методов, объявлений и выражений.
  • Он упрощает генерацию кода и использует один поддерживаемый механизм для генерации и загрузки кода, не ограничивая вас использованием исходного кода на базе файлов.
  • Он переносим между текущими и будущими реализациями JDK версии 6 и выше от различных производителей.
  • Он использует проверенную версию компилятора Java.
  • В отличие от систем, основанных на интерпретации, загруженные классы могут пользоваться всеми оптимизациями, выполняемыми JRE во время исполнения.

Компиляция Java: принципы и реализация

Для понимания пакета javax.tools полезно ознакомиться с принципами компиляции в Java и тем, как они реализуются в пакете. Пакет javax.tools предоставляет абстракции для всех этих концепций в общем виде, что позволяет вводить исходный код из различных объектов-источников, а не только из файловой системы.

Компиляция исходного кода требует следующих компонентов:

  • Объект (переменная) classpath (путь к классам), через которую компилятор может найти классы библиотек. Объект classpath для компилятора обычно состоит из упорядоченного списка каталогов файлов системы и архивных файлов (JAR или ZIP), которые содержат уже скомпилированные .class файлы. Объект classpath реализуется объектом типа JavaFileManager, который управляет множество объектов JavaFileObject со скомпилированными классами и исходным кодом и объекта типа ClassLoader, передаваемым в конструктор JavaFileManager. JavaFileObject это потомок FileObject, специализированный с помощью одного из вариантов значения перечисляемого типа (enum) JavaFileObject.Kind, полезного для компилятора:
    • SOURCE (исходный код)
    • CLASS (скомпилированный класс)
    • HTML
    • OTHER (другое)
    Каждый файл с исходным кодом предоставляет метод openInputStream() для доступа к исходному коду как к объекту типа InputStream.
  • javac options (параметры компилятора javac), которые передаются в объекте типа Iterable<String>
  • Source files (файлы с исходным кодом) — один или несколько исходных .java-файлов для компиляции. Объект JavaFileManager предоставляет абстрактную файловую систему, которая привязывает имена исходных и получившихся в результате файлов к экземплярам объектов JavaFileObject. В данном случае файл означает связь между уникальным именем и последовательностью байтов. Клиенту не обязательно использовать настоящую файловую систему. В примерах в статье объект JavaFileManager управляет привязками между именами классов и объектами CharSequence, содержащими исходный код Java для компиляции. Объект типа JavaFileManager.Location содержит имя файла и флаг, показывающий это местоположение исходного кода или местоположение результирующего скомпилированного кода. Класс ForwardingJavaFileManager реализует шаблон Chain of Responsibility (цепочка ответственности) (см. раздел Ресурсы), позволяющий связывать между собой менеджеры, как путь к классам (classpath) и пути к исходному коду (source paths) соединяют вместе JAR-файлы и каталоги. Если Java-класс не обнаруживается в первом элементе цепочки, поиск делегируются остальным элементам в цепочке.
  • Output directories (каталоги с выходными файлами), куда компилятор записывает сгенерированные .class-файлы. Действуя как коллекция полученных в результате .class-файлов, объект JavaFileManager также хранит объекты JavaFileObject, представляющие скомпилированные CLASS-файлы.
  • compiler (компилятор). Класс JavaCompiler создает объекты JavaCompiler.CompilationTask, которые компилируют исходный код из объектов JavaFileObjectSOURCE в объекте JavaFileManager, создавая новые выходные JavaFileObject CLASS файлы и объекты типа Diagnostic (предупреждения и ошибки). Статический метод ToolProvider.getSystemJavaCompiler() возвращает экземпляр компилятора.
  • Compiler warnings and errors (сообщения от компилятора с предупреждениями и ошибками), которые реализуются классами Diagnostic и DiagnosticListener. Объект типа Diagnostic – это одно предупреждение или ошибка компиляции, выданное компилятором. Объект Diagnostic определяет:
    • KIND (тип): ERROR (ошибка), WARNING (предупреждение), MANDATORY_WARNING (обязательное предупреждение), NOTE (примечание) или OTHER (другое);
    • Местонахождение исходного кода (включая номер строки и столбца);
    • Сообщение.
    Клиент предоставляет компилятору объект типа DiagnosticListener, через который компилятор передает диагностические сообщения обратно клиенту. Класс DiagnosticCollector – это простая реализация DiagnosticListener.

На рисунке 1 показана связь принципов компилятора javac с их реализацией в javax.tools:

Рисунок 1. Как принципы javac связаны с интерфейсами пакета javax.tools
Рисунок 1. Как принципы javac связаны с интерфейсами пакета javax.tools

Изучив эти концепции, посмотрим теперь, как с их помощью реализовать фасад для компиляции объектов CharSequences.


Компиляция исходного кода Java в объектах CharSequence

В этом разделе будет сконструирован фасад для класса javax.tools.JavaCompiler. Класс javaxtools.compiler.CharSequenceCompiler (см. Загрузка) может компилировать исходный код Java в любых объектах java.lang.CharSequence (таких как String, StringBuffer и StringBuilder), возвращая объект типа Class. Класс CharSequenceCompiler имеет следующий API:

  • public CharSequenceCompiler(ClassLoader loader, Iterable<String> options): Этот конструктор принимает объект типа ClassLoader, который передается компилятору Java, позволяя ему находить зависимые классы. Параметр типа Iterable позволяет клиенту передать дополнительные параметры компилятора, которые соответствуют опциям компилятора javac.
  • public Map<String, Class<T>> compile(Map<String, CharSequence> classes, final DiagnosticCollector<JavaFileObject> diagnostics) throws CharSequenceCompilerException, ClassCastException: Это стандартный метод для компиляции, который поддерживает одновременную компиляцию нескольких элементов исходного кода. Стоит отметить, что компилятор Java должен поддерживать циклические графы из классов, например, когда A.java зависит от B.java, B.java зависит от C.java, а C.java зависит от A.java. Первый аргумент этого метода – объект Map, ключи которого – полные имена классов, а соответствующие им значения – объекты CharSequences, содержащие исходный код этого класса. Например:
    • "mypackage.A" "package mypackage; public class A { ... }";
    • "mypackage.B" "package mypackage; class B extends A implements C { ... }";
    • "mypackage.C" "package mypackage; interface C { ... }"
    Компилятор добавляет объекты Diagnostics в DiagnosticCollector. Параметр типа T - это обобщенный тип, в который нужно преобразовать класс. Метод compile() перегружается другим методом, который принимает одно имя класса и объект CharSequence для компиляции.
  • public ClassLoader getClassLoader(): Этот метод возвращает загрузчик классов, который компилятор создал во время генерации .class-файлов, чтобы из него можно было загружать другие классы или ресурсы.
  • public Class<T> loadClass(final String qualifiedClassName) throws ClassNotFoundException: Так метод compile() может определить несколько классов, включая вложенные public-классы, этот метод позволяет загрузить эти вспомогательные классы.

Для поддержки API CharSequenceCompiler я реализую интерфейсы пакета javax.tools с классами JavaFileObjectImpl (для хранения объектов CharSequence с исходным кодом и выходных CLASS-объектов, созданных компилятором) и JavaFileManagerImpl (который привязывает имена к экземплярам JavaFileObjectImpl для управления последовательностями исходного кода и байт-кода, созданного компилятором).

JavaFileObjectImpl

Класс JavaFileObjectImpl, показанный в листинге 1, реализует интерфейс JavaFileObject и хранит исходный код в объекте CharSequence (SOURCE-объекты) или байт-код в объекте ByteArrayOutputStream (CLASS-файлы). Ключевой метод - CharSequence getCharContent(final boolean ignoreEncodingErrors), через который компилятор получает текст исходного кода. Полный исходный код всех примеров приведен в разделе Загрузка.

Листинг 1. Класс JavaFileObjectImpl (фрагмент исходного кода)
final class JavaFileObjectImpl extends SimpleJavaFileObject {
   private final CharSequence source;

   JavaFileObjectImpl(final String baseName, final CharSequence source) {
      super(CharSequenceCompiler.toURI(baseName + ".java"), Kind.SOURCE);
      this.source = source;
   }
   @Override
   public CharSequence getCharContent(final boolean ignoreEncodingErrors)
         throws UnsupportedOperationException {
      if (source == null)
         throw new UnsupportedOperationException("getCharContent()");
      return source;
   }
}

FileManagerImpl

Класс FileManagerImpl (см. листинг 2) расширяет класс ForwardingJavaFileManager для привязки полных имен классов к объектам JavaFileObjectImpl:

Листинг 2. Класс FileManagerImpl (фрагмент исходного кода)
final class FileManagerImpl extends ForwardingJavaFileManager<JavaFileManager> {
   private final ClassLoaderImpl classLoader;
   private final Map<URI, JavaFileObject> fileObjects 
           = new HashMap<URI, JavaFileObject>();

   public FileManagerImpl(JavaFileManager fileManager, ClassLoaderImpl classLoader) {
      super(fileManager);
      this.classLoader = classLoader;
   }

   @Override
   public FileObject getFileForInput(Location location, String packageName,
         String relativeName) throws IOException {
      FileObject o = fileObjects.get(uri(location, packageName, relativeName));
      if (o != null)
         return o;
      return super.getFileForInput(location, packageName, relativeName);
   }

   public void putFileForInput(StandardLocation location, String packageName,
         String relativeName, JavaFileObject file) {
      fileObjects.put(uri(location, packageName, relativeName), file);
   }
}

CharSequenceCompiler

Если метод ToolProvider.getSystemJavaCompiler() не может создать объект JavaCompiler

Метод ToolProvider.getSystemJavaCompiler() может вернуть null, если путь к файлу tools.jar не находится в переменной classpath приложения. Класс CharStringCompiler обнаруживает эту возможную конфигурационную проблему и выдает исключительную ситуацию с рекомендацией по устранению этой проблемы. Стоит отметить, что лицензия Sun позволяет распространять файл tools.jar вместе с JRE.

Теперь с этими вспомогательными классами можно определить класс CharSequenceCompiler. Он создается с объектом ClassLoader (загрузчиком классов времени исполнения) и параметрами компилятора. В нем используется метод ToolProvider.getSystemJavaCompiler() для получения экземпляра JavaCompiler, затем создается JavaFileManagerImpl, который перенаправляется в стандартный менеджер файлов компилятора.

Метод compile() проходит по поданной на вход таблице ключ-значение, создавая объекты JavaFileObjectImpl из каждой пары имя/объект CharSequence и добавляя их в JavaFileManager, чтобы JavaCompiler нашел их при вызове метода getFileForInput() менеджера файлов. Метод compile() затем создает объект JavaCompiler.Task и запускает его. Сбои приводят к выдаче исключительной ситуации CharSequenceCompilerException. Далее для каждого элемента исходного кода, переданного в метод compile(), загружается полученный объект Class и помещается в выходную коллекцию типа Map.

Загрузчик классов, связанный с CharSequenceCompiler (см. листинг 3), - это объект типа ClassLoaderImpl, который ищет байт-код для класса в объекте типа JavaFileManagerImpl, возвращая .class файлы, созданные компилятором:

Листинг 3. Класс CharSequenceCompiler (фрагмент исходного кода)
public class CharSequenceCompiler<T> {
   private final ClassLoaderImpl classLoader;
   private final JavaCompiler compiler;
   private final List<String> options;
   private DiagnosticCollector<JavaFileObject> diagnostics;
   private final FileManagerImpl javaFileManager;

   public CharSequenceCompiler(ClassLoader loader, Iterable<String> options) {
      compiler = ToolProvider.getSystemJavaCompiler();
      if (compiler == null) {
         throw new IllegalStateException(
               "Cannot find the system Java compiler. "
               + "Check that your class path includes tools.jar");
      }
      classLoader = new ClassLoaderImpl(loader);
      diagnostics = new DiagnosticCollector<JavaFileObject>();
      final JavaFileManager fileManager = compiler.getStandardFileManager(diagnostics,
            null, null);
      javaFileManager = new FileManagerImpl(fileManager, classLoader);
      this.options = new ArrayList<String>();
      if (options != null) {
         for (String option : options) {
            this.options.add(option);
         }
      }
   }

   public synchronized Map<String, Class<T>> 
	      compile(final Map<String, CharSequence> classes,
                  final DiagnosticCollector<JavaFileObject> diagnosticsList)
          throws CharSequenceCompilerException, ClassCastException {
      List<JavaFileObject> sources = new ArrayList<JavaFileObject>();
      for (Entry<String, CharSequence> entry : classes.entrySet()) {
         String qualifiedClassName = entry.getKey();
         CharSequence javaSource = entry.getValue();
         if (javaSource != null) {
            final int dotPos = qualifiedClassName.lastIndexOf('.');
            final String className = dotPos == -1 
	              ? qualifiedClassName
                  : qualifiedClassName.substring(dotPos + 1);
            final String packageName = dotPos == -1 
	              ? "" 
                  : qualifiedClassName .substring(0, dotPos);
            final JavaFileObjectImpl source = 
	              new JavaFileObjectImpl(className, javaSource);
            sources.add(source);
            javaFileManager.putFileForInput(StandardLocation.SOURCE_PATH, packageName,
                  className + ".java", source);
         }
      }
      final CompilationTask task = compiler.getTask(null, javaFileManager, diagnostics,
                                                    options, null, sources);
      final Boolean result = task.call();
      if (result == null || !result.booleanValue()) {
         throw new CharSequenceCompilerException("Compilation failed.", 
                                                 classes.keySet(), diagnostics);
      }
      try {
         Map<String, Class<T>> compiled = 
	                    new HashMap<String, Class<T>>();
         for (Entry<String, CharSequence> entry : classes.entrySet()) {
            String qualifiedClassName = entry.getKey();
            final Class<T> newClass = loadClass(qualifiedClassName);
            compiled.put(qualifiedClassName, newClass);
         }
         return compiled;
      } catch (ClassNotFoundException e) {
         throw new CharSequenceCompilerException(classes.keySet(), e, diagnostics);
      } catch (IllegalArgumentException e) {
         throw new CharSequenceCompilerException(classes.keySet(), e, diagnostics);
      } catch (SecurityException e) {
         throw new CharSequenceCompilerException(classes.keySet(), e, diagnostics);
      }
   }
}

Приложение Plotter

Теперь, когда имеется простой API для компиляции исходного кода, используем его для создания приложения, строящего графики функций и написанного с помощью Swing. На рисунке 2 показано приложение, рисующее график функции x * sin(x) * cos(x):

Рисунок 2. Динамическое приложение, использующее пакет javax.tools.compiler
Рисунок 2. Динамическое приложение, использующее пакет javaxtools.compiler

Приложение использует интерфейс Function, определенный в листинге 4:

Листинг 4. Интерфейс Function
package javaxtools.compiler.examples.plotter;
public interface Function {
   double f(double x);
}

Приложение предоставляет текстовое поле, куда пользователь может ввести выражение Java, возвращающее значение типа double, основанное на неявно объявленном входном параметре x типа double. Приложение вставляет текст этого выражения в шаблон кода, показанный в листинге 5, в место, помеченное как $expression. Оно также каждый раз генерирует уникальное имя класса, заменяя выражение $className в шаблоне. Имя пакета – это также переменная в шаблоне.

Листинг 5. Шаблон на основе интерфейса Function
package $packageName;
import static java.lang.Math.*;
public class $className
             implements javaxtools.compiler.examples.plotter.Function {
  public double f(double x) { 
    return ($expression) ; 
  }
}

Приложение заполняет шаблон с помощью метода fillTemplate(packageName, className, expr), который возвращает объект типа String, который затем компилируется с помощью CharSequenceCompiler. Исключительные ситуации или диагностические сообщения от компилятора передаются в метод log() или записываются непосредственно в прокручиваемый GUI-компонент errors в окне приложения.

Метод newFunction(), показанный в листинге 6, возвращает объект, реализующий интерфейс Function (см. шаблон исходного кода в листинге 5):

Листинг 6. Метод newFunction(String expr), возвращающий объект Function
Function newFunction(final String expr) {
   errors.setText("");
   try {
      // генерация уникальных имен класса и пакета для обеспечения безопасности
      final String packageName = PACKAGE_NAME + digits();
      final String className = "Fx_" + (classNameSuffix++) + digits();
      final String qName = packageName + '.' + className;
      // генерация класса исходного кода в виде объекта String
      final String source = fillTemplate(packageName, className, expr);
      // компиляция сгенерированного Java-кода
      final DiagnosticCollector<JavaFileObject> errs =
            new DiagnosticCollector<JavaFileObject>();
      Class<Function> compiledFunction = stringCompiler.compile(qName, source, errs,
            new Class<?>[] { Function.class });
      log(errs);
      return compiledFunction.newInstance();
   } catch (CharSequenceCompilerException e) {
      log(e.getDiagnostics());
   } catch (InstantiationException e) {
      errors.setText(e.getMessage());
   } catch (IllegalAccessException e) {
      errors.setText(e.getMessage());
   } catch (IOException e) {
      errors.setText(e.getMessage());
   }
   return NULL_FUNCTION;
}

Обычно приходится генерировать классы с исходным кодом, которые расширяют известный базовый класс или реализуют конкретный интерфейс, так что можно преобразовывать объекты к известному типу и вызывать их методы через корректный API. Отметим, что класс Function использовался в качестве параметризованного типа T при создании объекта типа CharSequenceCompiler<T>. Это позволяет объявить ссылку compiledFunction типа Class<Function>, а выражению compiledFunction.newInstance() возвращать объект типа Function без преобразований.

После того как был получен динамически сгенерированный экземпляр Function, приложение использует его для генерации Y-значений для диапазона X-значений, а затем выводит график значений функции (X, Y) с помощью JFreeChart API с открытым кодом API (см. раздел Ресурсы). Полный листинг Swing-приложения имеется в загружаемом исходном коде в пакете javaxtools.compiler.examples.plotter.

У этого приложения достаточно скромная потребность в генерации исходного кода. Другие приложения могут использовать более сложные механизмы исходного кода, например, Apache Velocity (см. раздел Ресурсы).


Риски и стратегии безопасности

Приложение, которое позволяет пользователю вводить произвольный исходный код Java, неизбежно порождает риски для безопасности. По аналогии с инжекцией SQL-кода (SQL injection) (см. раздел Ресурсы) система, которая позволяет пользователю или другому агенту предоставлять необработанный исходный код Java для генерации байт-кода, может быть поставлена под угрозу. Например, в приложении Plotter, представленном в этой статье, корректное Java-выражение может содержать анонимные вложенные классы, которые попробуют получить доступ к системным ресурсам, запустить потоки для DOS-атаки (отказ в обслуживании) и выполнить другие враждебные действия. Эта уязвимость называется инжекцией Java-кода (Java injection). Такие приложения нельзя устанавливать в небезопасные места, где непроверенные пользователи смогут получить к ним доступ (например, в качестве сервлета на Java EE-сервер или в виде апплета). Кроме того, как правило, клиенты пакета javax.tools должны ограничивать ввод со стороны пользователя и транслировать запросы пользователя в безопасный исходный код.

Стратегии для обеспечения безопасности при использовании этого пакета включают в себя:

  • Использование специальных объектов SecurityManager или ClassLoader, предотвращающих загрузку анонимных классов или других классов, которые находятся вне вашего непосредственного контроля.
  • Использовать сканер исходного кода или другой препроцессор, чтобы отвергать введенные данные, содержащие подозрительные конструкции кода. Например, приложение Plotter может использовать класс java.io.StreamTokenizer и отбрасывать введенные данные, которые содержат символ { (левую фигурную скобку), эффективно предотвращая объявление анонимных вложенных классов.
  • Использование javax.tools API, например, класс JavaFileManager может отказать в записи любого непредвиденного CLASS файла. При компиляции конкретного класса JavaFileManager может выдавать исключительную ситуацию SecurityExeception для всех вызовов при сохранении непредвиденных .class файлов и допускать только сгенерированные имена классов и пакетов, которые пользователь не сможет предугадать или подменить. Эта стратегия используется в методе newFunction приложения Plotter.

Заключение

В этой статье были рассмотрены принципы и важнейшие интерфейсы пакета javax.tools, был показан фасад для компиляции исходного кода Java, хранящегося в объектах String или CharSequences, а также использование библиотечного класса для разработки примера приложения, выводящего график заданной функции f(x). Другие полезные возможности применения этой техники:

  • Генерация двоичного кода чтения/записи файлов из описания на языке описания данных.
  • Генерация трансляторов форматов, подобных JAXB (Java Architecture for XML Binding – Java архитектура для привязки к XML) или каркасов для обеспечения персистентности объектов (JPA, Hibernate).
  • Реализация интерпретаторов доменно-ориентированных языков, выполняющих трансляцию доменно-ориентированного исходного кода в код на языке Java, за которой следует компиляция исходного кода на Java и загрузка классов, как это делается в JSP.
  • Реализация систем, основанных на правилах.
  • Любые другие мыслимые применения.

Когда у вас в следующий раз при разработке приложения возникнет потребность в динамическом поведении, обратите внимание на разнообразные и гибкие возможности, предоставляемые пакетом javax.tools.


Загрузка

ОписаниеИмяРазмер
пример предложения для этой статьиj-jcomp.zip166KB

Ресурсы

Научиться

  • Create dynamic applications with javax.tools: оригинал статьи (EN).
  • javax.tools: документация Javadoc для API пакета.
  • JSR 199: Java Compiler API (EN): исходный Java Specification Request, по которому был разработан пакет javax.tools в рамках Java Community Process.
  • Java design patterns 101 (EN) (David Gallardo, developerWorks, январь 2002 г.): статья, описывающая шаблоны Facade и Chain of Responsibility, впервые представленные в книге Design Patterns: Elements of Reusable Object-Oriented Design (Erich Gamma et al., Addison-Wesley, 1994).
  • Call components safely (EN) (David Wheeler, developerWorks, декабрь 2004 г.): в этой статье рассматривается инжекция SQL, уязвимость в безопасности, которая позволяет пользователям задавать неформатированный текст, вставляющийся затем в SQL выражения, которые потом загружаются в базу данных.
  • Invoke dynamic languages dynamically (EN) (Tom McQueeney, developerWorks, сентябрь 2007г.): в этой серии из двух статей описывается другой способ, с помощью которого Java SE 6 упрощает разработку динамической функциональности для приложений.
  • Safari bookstore: сайт магазина книг по ИТ.
  • Раздел Технология Java сайта developerWorks: сотни статей обо всех аспектах Java-программирования.

Получить продукты и технологии

  • Apache Velocity:: процессор шаблонов, который можно использовать для более гибкой и сложной генерации исходного кода Java.
  • Apache Commons JCI: уже имеющийся API для обеспечения доступа к Java компилятору.
  • Janino:: Janino предоставляет возможности схожие с пакетом javax.tools, но данный момент он ограничен совместимостью только с исходным кодом Java 1.3.
  • Javassist: Javassist обеспечивает динамическое создание и загрузку файлов Java-классов, но делает это через модель байт-кода, а не исходного кода Java.
  • JFreeChart: API для построения диаграмм, использовавшийся в примере в статье.
  • Скачайте ознакомительные версии продуктов IBM и опробуйте на практике средства для разработки приложений и связующее программное обеспечение DB2®, Lotus®, Rational®, Tivoli® и WebSphere.

Обсудить

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы первый раз заходите в developerWorks. Информация в вашем профиле (имя, страна / регион, название компании) отображается для всех пользователей и будет сопровождать любой опубликованный вами контент пока вы специально не укажите скрыть название вашей компании. Вы можете обновить ваш IBM аккаунт в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Технология Java
ArticleID=447276
ArticleTitle=Создание динамических приложений с помощью javax.tools
publish-date=11162009