我正在寻找一种方法来(重新)将一个爆炸的捆绑包(意思是不是在文件夹中),从Eclipse中运行的Apache Felix OSGi容器,最好是使用启动任务。
我发现了this问题,答案很接近,但这取决于在Gogo shell中键入命令,这对于长期开发使用不方便。我想为此使用Eclipse的启动任务机制,但如果有同样快速和方便的替代方案,我也会对此持开放态度。
现在我认为如果我可以从Eclipse启动任务中发出Gogo shell命令,那将是一个解决方案,但我也无法理解如何做到这一点。我认为我需要Remote Shell捆绑包吗?
我开始考虑用Java编写一个telnet客户端,它可以连接到Remote Shell软件包并以自动方式执行Gogo命令。我已经看过some example已经可以修改以满足我的需要......但是我正在从中获得一种“重新发明轮子”的感觉。当然有更好的方法吗?
帮助您了解我在做什么的一些背景知识:
我已经建立了一个Eclipse'OSGiContainer'项目,该项目基本上包含Apache Felix jar和我想部署的第三方软件包(如Gogo shell),类似于here所描述的项目设置。然后我创建了第二个包含我的包的'MyBundle'项目。我想通过启动OSGiContainer项目启动OSGi容器,然后在我的bundle上开发并测试我的更改,方法是将MyBundle项目启动到OSGiContainer中,我只想在开发过程中一直运行。
项目布局:
然后我可以通过在Gogo shell上调用这些命令将我的bundle部署到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方法,带有必要的命令来卸载bundle然后重新安装并启动它。 bundle的路径应作为程序的参数给出,如下所示:
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());
}
}
}
答案 0 :(得分:1)
当我遇到相同的要求(尽可能快地从目标/类部署bundle)时,我的第一个想法也是扩展我的容器的一些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,因为我希望将来有更多功能(而不仅仅是部署):)