Как мы можем объединить некоторые преимущества динамических языков со строгой типизацией в обычном Java-коде? Все самое интересное на хабе обычно происходит в комментариях к статье.
Вот и на этот раз в комментариях к «Модуляризация в JavaSE без OSGI и Jigsaw» началась дискуссия о том, что работа через рефлексию в java сводит на нет многие преимущества библиотеки МВН-загрузчик классов .
В Groovy работать с этой библиотекой просто и удобно:
Давайте попробуем это исправить с помощью java-as-script.jar , исходный код проекта доступен по адресу github .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')
Динамическая компиляция
Стандарт 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 #Сборка систем
-
Лингвистическая География
19 Oct, 24 -
Kindle Для Слабовидящих
19 Oct, 24 -
Тостер. Коллективное Редактирование Тегов
19 Oct, 24