実行時のJavaプログラムのコンパイルと依存関係の解決

動的言語のいくつかの利点と、通常のJavaコードの強力な型付けをどのように組み合わせることができますか?



Habréで最も興味深いのは、通常記事へのコメントです。 そのため、今回は、 「OSGIとジグソーを使用しないJavaSEでのモジュール化」へのコメントで、javaのリフレクションによる作業がmvn-classloaderライブラリの多くの利点を打ち消すという議論が始まりました。 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')
      
      





プロジェクトのソースコードがgithubにある java-as-script.jarを使用して修正してみましょう。



動的コンパイル



JSR 199標準はjavaコンパイラAPIであり、かなり以前から存在しています。 APIは、javaパッケージjavax.tools。*に含まれています。 ただし、Javaコードをメモリからメモリにコンパイルして実行するには、きれいなコードを作成してタンバリンに打ち込む必要があります。 コンパイラの実装はJREの一部として行われず、tools.jarはMavenリポジトリにありません。



毎回サイクルするのではなく、何か準備ができていて、同僚がジャニーノプロジェクトを提案した。 Janino自体にはjavaのサブセットの独自のコンパイラが含まれており、式の計算にのみ適しています。 JSR 199を使用するorg.codehaus.janino:commons-compiler-jdkがありますが、これはoracle / openjdk tools.jarのみに大きく依存しています。 このプロジェクトを完了したjava-as-scriptには、Eclipse javaコンパイラと、そのために変更されたcommons-compiler-jdkが含まれています。 自己完結型であり、JREでもJava 8コードをコンパイルおよびロードできます。



動的依存関係の解決



Groovyには、Mavenリポジトリーから依存関係をスクリプトに追加できる便利なGrapeメカニズムがあります。



javaコンパイラとmvn-classloader組み合わせると、ソースコードのコメントを使用してJavaプログラムに依存関係を追加できます。



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





実用的な例を挙げましょう。 RaspberryPiアラームコードを実行するには、 1つのjavaファイルで十分です。 コマンドラインから例を実行できます。



 java -Dlogin=...YOUR_EMAIL...@mail.ru -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(); } }
      
      





コードのリフレクションを取り除くには、mvn-classloaderローダーからクラスキャストが行われるAPIをスクリプトの依存関係で指定し、スクリプトクラスをロードしたローダーを親ローダーとして指定する必要があります。



既存の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パラメーターを使用してプログラムを実行できます

現在のディレクトリに、mavenのpomファイルと、ソースコードを含むすべての必要なディレクトリが作成されます。 これにより、通常のIDEでJavaコードを操作してデバッグするためのすべての機能を備えたスクリプトを開発できます。



java-as-script は、数百のjava.net.URLプロトコルのいずれか使用してプログラムのソースコードをロードし、コメント//依存関係:mvn:/として指定されたJavaスクリプトの依存関係を解決し、これらの依存関係でソースコードをコンパイルし、クラスをロードし、開始しますその主な方法。 この場合、リモートデバッガーを使用して接続し、通常のJavaプログラムとしてスクリプトをデバッグできます。



All Articles