Вызов clojure от Java

Большинство главных хитов Google для "вызова clojure от Java" устарело и рекомендует использовать clojure.lang.RT скомпилировать исходный код. Вы могли помочь с четким объяснением того, как звонить, Clojure от Java, принимающего Вас, уже создали банку из проекта Clojure и включали его в путь к классу?

161
задан dskrvk 31 May 2015 в 05:11
поделиться

4 ответа

Если программа установки не регистрирует COM-объекты с помощью механизма по умолчанию, попробуйте принудительно выполнить это действие. Дополнительным преимуществом этой техники является то, что вы можете один шаг через пользовательское действие, чтобы выяснить, что происходит.

Добавьте в проект класс, который наследует от установщика, имеет этот атрибут

[RunInstaller(true)]  

и переопределяет один или несколько из следующих методов:

Install()
Uninstall()
Commit()
Rollback()

Вот полный пример, который выполняет регистрацию в COM Interop. Это посыпано трассировкой выходных данных, которые (вопреки некоторым мнениям) появятся в Sysinternals DebugVw. При создании конфигурации Debug появится диалоговое окно, в котором можно подключить отладчик и выполнить настраиваемое действие за один шаг.

using System.ComponentModel;
using System.Configuration.Install;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace TiGra
    {
    /// <summary>
    /// Custom install actions that must be carried out during product installation.
    /// </summary>
    [RunInstaller(true)]
    public class MyInstaller : Installer
        {

        /// <summary>
        /// Custom Install Action that performs custom registration steps as well as
        /// registering for COM Interop.
        /// </summary>
        /// <param name="stateSaver">Not used<see cref="Installer"/></param>
        public override void Install(System.Collections.IDictionary stateSaver)
            {
            Trace.WriteLine("Install custom action - Starting registration for COM Interop");
#if DEBUG
            MessageBox.Show("Attach debugger to this process now, if required", "Custom Action Debug", MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
#endif
            base.Install(stateSaver);
            RegistrationServices regsrv = new RegistrationServices();
            if (!regsrv.RegisterAssembly(this.GetType().Assembly, AssemblyRegistrationFlags.SetCodeBase))
                {
                Trace.WriteLine("COM registration failed");
                throw new InstallException("Failed To Register for COM Interop");
                }
            Trace.WriteLine("Completed registration for COM Interop");
            }

        /// <summary>
        /// Custom Install Action that removes the COM Interop component registrations.
        /// </summary>
        /// <param name="savedState">Not used<see cref="Installer"/></param>
        public override void Uninstall(System.Collections.IDictionary savedState)
            {
            Trace.WriteLine("Uninstall custom action - unregistering from COM Interop");
            try
                {
                base.Uninstall(savedState);
                RegistrationServices regsrv = new RegistrationServices();
                if (!regsrv.UnregisterAssembly(this.GetType().Assembly))
                    {
                    Trace.WriteLine("COM Interop deregistration failed");
                    throw new InstallException("Failed To Unregister from COM Interop");
                    }
                }
            finally
                {
                Trace.WriteLine("Completed uninstall custom action");
                }
            }
        }
    }

Осталось сделать одно последнее. Настраиваемые действия не будут выполняться, если программа установки не настроена на это.

  • В проекте установщика Visual Studio щелкните правой кнопкой мыши имя проекта и выберите Просмотр - > Пользовательские действия. Вы увидите древовидное представление примерно так:
    • Настраиваемые действия
      • Install
      • Uninstall
      • Commit
      • Rollback
  • Щелкните правой кнопкой мыши на самом верхнем узле (Custom Actions) и выберите Add Custom Action .
  • Перейдите к файлу или выходному файлу проекта, содержащему класс с атрибутом [RunInstaller (true)], выделите его и нажмите кнопку «ОК».
  • Выходные данные проекта должны отображаться под каждым из четырех узлов. Это означает, что пользовательский класс действий будет вызываться на каждом из четырех этапов программы установки.

Если требуется более точный контроль над этим, можно добавить пользовательский класс действий к некоторым фазам программы установки, но не к другим. Например, если установка и удаление обрабатываются различными сборками, необходимо добавить одну сборку в узлы Установка и Фиксация, а другую сборку в узлы Удаление и Откат.

Таким образом, пользовательские действия будут вызваны во время установки. Быстрый наконечник по отладке пользовательских действий. Используйте условную директиву (как в приведенном выше примере) для отображения окна сообщения при построении отладки. Это окно сообщения будет отображаться во время настройки. Это фактически приостанавливает процесс установки до тех пор, пока вы не нажмете кнопку OK в окне сообщения, это даст вам то, что вы можете назвать «окно возможностей» (каламбур предназначен) для присоединения отладчика к процессу msiexec.exe. Будет запущено несколько процессов msiexec.exe, нужно выбрать тот, который говорит, что это управляемый код. Отладчик подключится, и точки останова «засветятся», так что вы сможете перехватить выполнение и один шаг в пользовательском действии.

-121--4349517-

Нет. Вы можете попытаться перекодировать страницы ASP с помощью компилятора ASP Classic Compiler .

-121--3879755-

Обновление : С момента публикации этого ответа некоторые доступные инструменты изменились. После первоначального ответа появляется обновление, включающее информацию о том, как построить пример с помощью текущих инструментов.

Это не так просто, как компиляция в банку и вызов внутренних методов. Кажется, есть несколько трюков, чтобы все это сработало. Вот пример простого файла Clojure, который может быть скомпилирован в банку:

(ns com.domain.tiny
  (:gen-class
    :name com.domain.tiny
    :methods [#^{:static true} [binomial [int int] double]]))

(defn binomial
  "Calculate the binomial coefficient."
  [n k]
  (let [a (inc n)]
    (loop [b 1
           c 1]
      (if (> b k)
        c
        (recur (inc b) (* (/ (- a b) b) c))))))

(defn -binomial
  "A Java-callable wrapper around the 'binomial' function."
  [n k]
  (binomial n k))

(defn -main []
  (println (str "(binomial 5 3): " (binomial 5 3)))
  (println (str "(binomial 10042 111): " (binomial 10042 111)))
)

Если вы его запустили, вы должны увидеть что-то вроде:

(binomial 5 3): 10
(binomial 10042 111): 49068389575068144946633777...

И вот программа Java, которая вызывает функцию -binomial в tiny.jar .

import com.domain.tiny;

public class Main {

    public static void main(String[] args) {
        System.out.println("(binomial 5 3): " + tiny.binomial(5, 3));
        System.out.println("(binomial 10042, 111): " + tiny.binomial(10042, 111));
    }
}

Это результат:

(binomial 5 3): 10.0
(binomial 10042, 111): 4.9068389575068143E263

Первый фрагмент магии использует ключевое слово : methods в операторе gen-class . Это необходимо для доступа к функции Clojure, например, к статическим методам Java.

Второе - создать функцию-оболочку, которая может быть вызвана Java. Обратите внимание, что вторая версия -биномиала имеет перед собой тире.

И, конечно, сама банка Clojure должна быть на пути класса. В этом примере используется Clojure-1.1.0 банка.

Обновление : Этот ответ был повторно протестирован с помощью следующих инструментов:

  • Clojure 1,5,1
  • Leiningen 2,1,3
  • JDK 1,7,0 Update 25

Clojure Part

Сначала создайте проект и связанную структуру каталогов с помощью Leiningen:

C:\projects>lein new com.domain.tiny

Теперь измените на каталог проекта.

C:\projects>cd com.domain.tiny

В каталоге проекта откройте файл project.clj и отредактируйте его таким образом, чтобы содержимое было таким, как показано ниже.

(defproject com.domain.tiny "0.1.0-SNAPSHOT"
  :description "An example of stand alone Clojure-Java interop"
  :url "http://clarkonium.net/2013/06/java-clojure-interop-an-update/"
  :license {:name "Eclipse Public License"
  :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.5.1"]]
  :aot :all
  :main com.domain.tiny)

Теперь убедитесь, что все зависимости (Clojure) доступны.

C:\projects\com.domain.tiny>lein deps

На данном этапе может появиться сообщение о загрузке банки Clojure.

Отредактируйте файл Clojure C :\projects\com.domain.tiny\src\com\domain\tiny.clj таким образом, чтобы он содержал программу Clojure, показанную в исходном ответе. (Этот файл был создан, когда Лейнинген создал проект.)

Большая часть магии здесь находится в объявлении пространства имен. : gen-class предписывает системе создать класс с именем com.domain.tiny с одним статическим методом, называемым binomial , функция, принимающая два целых аргумента и возвращающая двойник. Существуют две аналогично именованные функции binomial , традиционная функция Clojure, и -binomial и обертка, доступная с Java. Обратите внимание на дефис в имени функции -биномиальный . Префикс по умолчанию - дефис, но при необходимости его можно изменить на другой. Функция -main просто делает пару вызовов биномиальной функции, чтобы гарантировать, что мы получаем правильные результаты. Для этого скомпилируйте класс и запустите программу.

C:\projects\com.domain.tiny>lein run

Выходные данные должны отображаться в исходном ответе.

Теперь упакуйте его в банку и положите в удобное место. Скопируйте там и банку Клоюре.

C:\projects\com.domain.tiny>lein jar
Created C:\projects\com.domain.tiny\target\com.domain.tiny-0.1.0-SNAPSHOT.jar
C:\projects\com.domain.tiny>mkdir \target\lib

C:\projects\com.domain.tiny>copy target\com.domain.tiny-0.1.0-SNAPSHOT.jar target\lib\
        1 file(s) copied.

C:\projects\com.domain.tiny>copy "C:<path to clojure jar>\clojure-1.5.1.jar" target\lib\
        1 file(s) copied.

Часть Java

Leiningen имеет встроенную задачу lein-javac , которая должна помочь с компиляцией Java. К сожалению, он кажется сломанным в версии 2.1.3. Не удается найти установленный JDK и репозиторий Maven.Пути к ним имеют встроенные места в моей системе. Полагаю, в этом и проблема. Любая Java IDE также может обрабатывать компиляцию и упаковку. Но для этого поста мы идем в старую школу и делаем это в командной строке.

Сначала создайте файл Main.java с содержимым, указанным в исходном ответе.

Для компиляции Java-части

javac -g -cp target\com.domain.tiny-0.1.0-SNAPSHOT.jar -d target\src\com\domain\Main.java

Теперь создайте файл с некоторой метаинформацией для добавления в банк, который мы хотим построить. В Manifest.txt добавьте следующий текст

Class-Path: lib\com.domain.tiny-0.1.0-SNAPSHOT.jar lib\clojure-1.5.1.jar
Main-Class: Main

Теперь упаковывайте его в один большой файл, включая нашу программу Clojure и банку Clojure.

C:\projects\com.domain.tiny\target>jar cfm Interop.jar Manifest.txt Main.class lib\com.domain.tiny-0.1.0-SNAPSHOT.jar lib\clojure-1.5.1.jar

Для запуска программы

C:\projects\com.domain.tiny\target>java -jar Interop.jar
(binomial 5 3): 10.0
(binomial 10042, 111): 4.9068389575068143E263

Выходные данные по существу идентичны выводимым Clojure, но результат был преобразован в Java-двойник.

Как уже упоминалось, Java IDE, вероятно, позаботится о грязных аргументах компиляции и упаковке.

158
ответ дан 23 November 2019 в 21:25
поделиться

Редактировать Этот ответ был написан в 2010 году и работал в то время. См. Ответ Алекса Миллера для более современного решения.

Какой код звонит от Java? Если у вас есть класс, созданный с классом Gen-Class, просто позвоните. Если вы хотите вызвать функцию из скрипта, посмотрите на в следующем примере .

Если вы хотите оценить код из строки, внутри Java, вы можете использовать следующий код:

import clojure.lang.RT;
import clojure.lang.Var;
import clojure.lang.Compiler;
import java.io.StringReader;

public class Foo {
  public static void main(String[] args) throws Exception {
    // Load the Clojure script -- as a side effect this initializes the runtime.
    String str = "(ns user) (defn foo [a b]   (str a \" \" b))";

    //RT.loadResourceScript("foo.clj");
    Compiler.load(new StringReader(str));

    // Get a reference to the foo function.
    Var foo = RT.var("user", "foo");

    // Call it!
    Object result = foo.invoke("Hi", "there");
    System.out.println(result);
  }
}
34
ответ дан 23 November 2019 в 21:25
поделиться

Компиляцию AOT можно также использовать для создания файлов классов, представляющих код clojure. Подробные сведения о том, как это сделать, см. в документации по компиляции, gen-class и friends в документах Clojure API, но в сущности вы создадите класс, который вызовет функции clojure для каждого вызова метода.

Другой альтернативой является использование новой функциональности дефпротокола и дефтипа, которая также потребует компиляции AOT, но обеспечит более высокую производительность. Я пока не знаю, как это сделать, но вопрос в списке рассылки, вероятно, сделает трюк.

-1
ответ дан 23 November 2019 в 21:25
поделиться

Другой метод, который работает также с другими языками поверх JVM, - это объявить интерфейс для функций, которые вы хотите вызвать, а затем использовать функцию «прокси» для создания экземпляр, который их реализует.

0
ответ дан 23 November 2019 в 21:25
поделиться
Другие вопросы по тегам:

Похожие вопросы: