1

我正在寻找一种方法来从 Eclipse 中(重新)部署一个分解的捆绑包(意味着不是 jarred,而是在一个文件夹中)到一个正在运行的 Apache Felix OSGi 容器,最好使用启动任务。

我发现了这个问题,它有一个接近的答案,但它取决于在 Gogo shell 中输入命令,这不方便长期开发使用。我想为此使用 Eclipse 的启动任务机制,但如果有同样快速和方便的替代方案,我也愿意接受。

现在我认为,如果我可以从 Eclipse 启动任务中触发 Gogo shell 命令,那将是一个解决方案,但我也无法理解如何做到这一点。我想我需要 Remote Shell 捆绑包,对吗?

我开始考虑用 Java 编写一个 telnet 客户端,它可以连接到 Remote Shell 包并以自动方式执行 Gogo 命令。我已经看到了一些 示例,我可以对其进行修改以满足我的需要……但是,我从中得到了一种“重新发明轮子”的感觉。肯定有更好的方法吗?

一些背景可以帮助您了解我在做什么:

我已经建立了一个 Eclipse 'OSGiContainer' 项目,它基本上包含 Apache Felix jar 和我想要部署的第三方包(如 Gogo shell),类似于此处描述的项目设置。然后我创建了第二个包含我的包的“MyBundle”项目。我想通过启动 OSGiContainer 项目来启动 OSGi 容器,然后在我的包上进行开发并通过将 MyBundle 项目启动到我只想在开发过程中一直运行的 OSGiContainer 中来测试我的更改。

项目布局:

  • OSGi容器
    • bin(包含 felix jar)
    • 捆绑包(第三方捆绑包)
    • conf(Felix 的 config.properties 文件)
  • 我的捆绑
    • 源代码
    • 目标
      • 班级

然后我可以通过在 Gogo shell 上调用这些命令将我的包部署到 OSGi 容器:

install reference:file:../MyBundle/target/classes
start <bundleId>

要重新部署,我调用这些命令:

stop <bundleId>
uninstall <bundleId>
install reference:file:../MyBundle/target/classes
start <bundleId>

您可以想象每次必须在 shell 上调用 4 个命令并没有那么有趣……因此,即使您可以给我一种方法来将其归结为更少的命令来键入,这已经是一个很大的改进了。

更新

我修改了一下,想出了下面的课程。它是对 telnet 示例的改编,做了一些小改动,并提供了一个带有必要命令的 main 方法,用于卸载捆绑包,然后重新安装并启动它。捆绑包的路径应作为程序的参数给出,如下所示:

reference:file:../MyBundle/target/classes

我仍然非常欢迎这个问题的答案,因为我根本不喜欢这个解决方案。然而,我已经证实这有效:

import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.net.SocketException;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;

import org.apache.commons.net.telnet.TelnetClient;

public class GogoDeployer {
    static class Responder extends Thread {
        private StringBuilder builder = new StringBuilder();
        private final GogoDeployer checker;
        private CountDownLatch latch;
        private String waitFor = null;
        private boolean isKeepRunning = true;

        Responder(GogoDeployer checker) {
            this.checker = checker;
        }

        boolean foundWaitFor(String waitFor) {
            return builder.toString().contains(waitFor);
        }

        public synchronized String getAndClearBuffer() {
            String result = builder.toString();
            builder = new StringBuilder();
            return result;
        }

        @Override
        public void run() {
            while (isKeepRunning) {
                String s;

                try {
                    s = checker.messageQueue.take();
                } catch (InterruptedException e) {
                    break;
                }

                synchronized (Responder.class) {
                    builder.append(s);
                }

                if (waitFor != null && latch != null && foundWaitFor(waitFor)) {
                    latch.countDown();
                }
            }
            System.out.println("Responder stopped.");
        }

        public String waitFor(String waitFor) {
            synchronized (Responder.class) {
                if (foundWaitFor(waitFor)) {
                    return getAndClearBuffer();
                }
            }

            this.waitFor = waitFor;
            latch = new CountDownLatch(1);
            try {
                latch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
                return null;
            }

            String result = null;
            synchronized (Responder.class) {
                result = builder.toString();
                builder = new StringBuilder();
            }

            return result;
        }
    }

    static class TelnetReader extends Thread {
        private boolean isKeepRunning = true;
        private final GogoDeployer checker;
        private final TelnetClient tc;

        TelnetReader(GogoDeployer checker, TelnetClient tc) {
            this.checker = checker;
            this.tc = tc;
        }

        @Override
        public void run() {
            InputStream instr = tc.getInputStream();

            try {
                byte[] buff = new byte[1024];
                int ret_read = 0;

                do {
                    if (instr.available() > 0) {
                        ret_read = instr.read(buff);
                    }
                    if (ret_read > 0) {
                        checker.sendForResponse(new String(buff, 0, ret_read));
                        ret_read = 0;
                    }
                } while (isKeepRunning && (ret_read >= 0));
            } catch (Exception e) {
                System.err.println("Exception while reading socket:" + e.getMessage());
            }

            try {
                tc.disconnect();
                checker.stop();
                System.out.println("Disconnected.");
            } catch (Exception e) {
                System.err.println("Exception while closing telnet:" + e.getMessage());
            }
        }
    }

    private static final String prompt = "g!";
    private static GogoDeployer client;


    private String host;
    private BlockingQueue<String> messageQueue = new LinkedBlockingQueue<String>();
    private int port;
    private TelnetReader reader;
    private Responder responder;
    private TelnetClient tc;

    public GogoDeployer(String host, int port) {
        this.host = host;
        this.port = port;
    }

    public void stop() {
        responder.isKeepRunning = false;
        reader.isKeepRunning = false;

        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
        }
        responder.interrupt();
        reader.interrupt();
    }

    public void send(String command) {
        PrintStream ps = new PrintStream(tc.getOutputStream());
        ps.println(command);
        ps.flush();
    }

    public void sendForResponse(String s) {
        messageQueue.add(s);
    }

    public void connect() throws SocketException, IOException {
        tc = new TelnetClient();
        tc.connect(host, port);
        reader = new TelnetReader(this, tc);
        reader.start();
        responder = new Responder(this);
        responder.start();
    }

    public String waitFor(String s) {
        return responder.waitFor(s);
    }

    private static String exec(String cmd) {
        String result = "";
        System.out.println(cmd);
        client.send(cmd);
        result = client.waitFor(prompt);
        return result;
    }

    public static void main(String[] args) {
        try {
            String project = args[0];
            client = new GogoDeployer("localhost", 6666);
            client.connect();
            System.out.println(client.waitFor(prompt));
            System.out.println(exec("uninstall " + project));
            String result = exec("install " + project);
            System.out.println(result);
            int start = result.indexOf(":");
            int stop = result.indexOf(prompt);
            String bundleId = result.substring(start + 1, stop).trim();
            System.out.println(exec("start " + bundleId));
            client.stop();
        } catch (SocketException e) {
            System.err.println("Unable to conect to Gogo remote shell: " + e.getMessage());
        } catch (IOException e) {
            System.err.println("Unable to conect to Gogo remote shell: " + e.getMessage());
        }
    }
}
4

1 回答 1

1

当我遇到相同的要求(尽可能快地从目标/类部署包)时,我的第一个想法也是用一些 shell 功能扩展我的容器。然而,我的第二个想法是编写一个简单的包,它打开一个始终在顶部的窗口,我可以简单地将任何项目从 Eclipse(或总指挥官或其他)拖放到该窗口。代码然后检查被删除的文件夹是否有一个目标/类文件夹,如果有,它将被部署。

源代码位于https://github.com/everit-org/osgi-richconsole

依赖项可从 maven-central 获得。

依赖是:

<dependency>
    <groupId>org.everit.osgi.dev</groupId>
    <artifactId>org.everit.osgi.dev.richconsole</artifactId>
    <version>1.2.0</version>
</dependency>

您可以在开发时使用该捆绑包,并在设置实时服务器时将其删除。但是,如果容器在无头模式下运行,则没有必要不显示弹出窗口。

我称它为richconsole,因为我希望将来有更多功能(不仅仅是部署):)

于 2013-04-07T23:25:24.120 回答