Компиляция Программ Java И Разрешение Зависимостей Во Время Выполнения.

Как мы можем объединить некоторые преимущества динамических языков со строгой типизацией в обычном Java-коде? Все самое интересное на хабе обычно происходит в комментариях к статье.

Вот и на этот раз в комментариях к «Модуляризация в JavaSE без OSGI и Jigsaw» началась дискуссия о том, что работа через рефлексию в java сводит на нет многие преимущества библиотеки МВН-загрузчик классов .

В Groovy работать с этой библиотекой просто и удобно:

  
  
  
  
  
   

def hawtIoConsole = MavenClassLoader.usingCentralRepo().

forMavenCoordinates('io.hawt:hawtio-app:2.0.0').

loadClass('io.hawt.app.App') Thread.currentThread().

setContextClassLoader(hawtIoConsole.getClassLoader()) hawtIoConsole.main('--port','10090')

Давайте попробуем это исправить с помощью java-as-script.jar , исходный код проекта доступен по адресу github .



Динамическая компиляция

Стандарт JSR 199, API компилятора Java, существует уже довольно давно.

Интерфейсы API присутствуют в пакетах Java. javax.tools.* .

Но для того, чтобы скомпилировать Java-код из памяти в память и затем запустить его, нужно изрядное количество пиши код и бей в бубен .

Реализация компилятора не включена в JRE, а файл tools.jar отсутствует в репозиториях maven. Хотелось что-то готовое, без цикличности каждый раз, а коллега предложил проект Янино .

Сам Janino содержит собственный компилятор подмножества Java и подходит только для оценки выражений.

Существует org.codehaus.janino:commons-compiler-jdk, который использует JSR 199, но он сильно зависит от oracle/openjdktools.jar. После завершения этого проекта java-as-script включает в себя Java-компилятор eclipse и модифицированный для него commons-compiler-jdk. Он автономен и позволяет компилировать и загружать код Java 8 даже в JRE.

Динамическое разрешение зависимостей

В Groovy есть удобный Виноградный механизм , который позволяет вам добавлять в ваш скрипт любые зависимости из репозиториев maven. Если вы объедините Java-компилятор и МВН-загрузчик классов , то зависимости можно добавить в программу Java, используя комментарии в исходном коде.



//dependency:mvn:/org.slf4j:slf4j-simple:1.6.6 //dependency:mvn:/org.apache.camel:camel-core:2.18.0

Позвольте мне привести вам рабочий пример.

Чтобы запустить код будильники на RaspberryPi достаточно один Java-файл .

Вы можете запустить пример из командной строки:

java -Dlogin=.

[email protected] -Dpassword=******* -jar java-as-script-1.1.jar https://raw.githubusercontent.com/igor-suhorukov/alarm-system/master/src/main/java/com/github/igorsuhorukov/alarmsys/AlarmSystem.java



package com.github.igorsuhorukov.alarmsys; // dependency:mvn:/com.github.igor-suhorukov:mvn-classloader:1.8 // dependency:mvn:/org.apache.camel:camel-core:2.18.0 // dependency:mvn:/org.apache.camel:camel-mail:2.18.0 // dependency:mvn:/io.rhiot:camel-webcam:0.1.4 // dependency:mvn:/io.rhiot:camel-pi4j:0.1.4 // dependency:mvn:/org.slf4j:slf4j-simple:1.6.6 import com.github.igorsuhorukov.smreed.dropship.MavenClassLoader; import org.apache.camel.Endpoint; import org.apache.camel.Exchange; import org.apache.camel.Processor; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.impl.DefaultAttachment; import org.apache.camel.impl.DefaultCamelContext; import org.apache.camel.management.event.CamelContextStartedEvent; import org.apache.camel.management.event.CamelContextStoppedEvent; import org.apache.camel.support.EventNotifierSupport; import javax.mail.util.ByteArrayDataSource; import java.lang.reflect.Method; import java.util.Date; import java.util.EventObject; class AlarmSystem { public static void main(String[] args) throws Exception{ String login = System.getProperty("login"); String password = System.getProperty("password"); DefaultCamelContext camelContext = new DefaultCamelContext(); camelContext.setName("Alarm system"); Endpoint mailEndpoint = camelContext.getEndpoint(String.format(" smtps://smtp.mail.ru:465Эusername=%s&password=%s&contentType=text/html&debugMode=true ", login, password)); camelContext.addRoutes(new RouteBuilder() { @Override public void configure() throws Exception { from(" pi4j-gpio://3Эmode=DIGITAL_INPUT&pullResistance=PULL_DOWN ").

routeId("GPIO read") .

choice() .

when(header("CamelPi4jPinState").

isEqualTo("LOW")) .

to(" controlbus:routeЭrouteId=RaspberryPI Alarm&action=resume") .

otherwise() .

to(" controlbus:routeЭrouteId=RaspberryPI Alarm&action=suspend"); from(" timer://capture_imageЭdelay=200&period=5000 ") .

routeId("RaspberryPI Alarm") .

to(" webcam:spycamЭresolution=HD720 ") .

setHeader("to").

constant(login) .

setHeader("from").

constant(login) .

setHeader("subject").

constant("alarm image") .

process(new Processor() { @Override public void process(Exchange it) throws Exception { DefaultAttachment attachment = new DefaultAttachment(new ByteArrayDataSource(it.getIn().

getBody(byte[].

class), "image/jpeg")); it.getIn().

setBody(String.format("<html><head></head><body><img src=\" cid:alarm-image.jpeg\ " /> %s</body></html>", new Date())); attachment.addHeader("Content-ID", "<alarm-image.jpeg>"); it.getIn().

addAttachmentObject("alarm-image.jpeg", attachment); //set CL to avoid javax.activation.UnsupportedDataTypeException: no object DCH for MIME type multipart/mixed Thread.currentThread().

setContextClassLoader( getClass().

getClassLoader() ); } }).

to(mailEndpoint); } }); registerLifecycleActions(camelContext, mailEndpoint, login); camelContext.start(); } }

Чтобы избавиться от отражения в коде, нужно в зависимостях скрипта указать API, к которому приводятся классы из загрузчика mvn-classloader, и указать в качестве родителя загрузчика загрузчик, загрузивший класс скрипта.

Реализовать запуск скрипта в существующей JVM-программе довольно просто:

org.github.suhorukov.java.as.script.ScriptRunner#runScript(String scriptText, String[] scriptArgs)

Если вам нужен загрузчик классов, просто позвоните

org.github.suhorukov.java.as.script.JavaCompiler#compileScript(java.lang.String scriptText)

а затем работать с классами из скрипта в своей программе.

Для быстрой отладки и генерации исходников pom.xml и java можно запустить программу с параметром -DgenerateMavenProjectAndExit=true. В текущем каталоге будет создан pom-файл для maven и все необходимые каталоги с исходным кодом.

Это позволяет разработать скрипт в привычной IDE со всеми его возможностями для работы с Java-кодом и его отладки.

java-as-script загружает исходный код программы по некоторым из сотен протоколов java.net.URL , разрешает зависимости Java-скрипта, указанные в комментариях //dependent:mvn:/, компилирует исходный код с этими зависимостями, загружает класс и запускает его основной метод. В этом случае вы можете подключиться с помощью удаленного отладчика и отлаживать скрипт как обычную Java-программу.

Теги: #java #janino #maven #компилятор #разрешение зависимостей #программирование #java #Groovy & Grails #Сборка систем

Вместе с данным постом часто просматривают:

Автор Статьи


Зарегистрирован: 2019-12-10 15:07:06
Баллов опыта: 0
Всего постов на сайте: 0
Всего комментарий на сайте: 0
Dima Manisha

Dima Manisha

Эксперт Wmlog. Профессиональный веб-мастер, SEO-специалист, дизайнер, маркетолог и интернет-предприниматель.