Fixed OSGI Shell broken in 00bbae07f2
- optional packages, not provided - they serve as an alternative
- cleanup
- without previous commit it was impossible to find the bug - in fact there
were two:
- enhanceForTarget originally returned it's parameter, not null
- inaccessible imports
Signed-off-by: David Matějček <dmatej@seznam.cz>
diff --git a/nucleus/common/common-util/osgi.bundle b/nucleus/common/common-util/osgi.bundle
index cda35f5..3f03166 100644
--- a/nucleus/common/common-util/osgi.bundle
+++ b/nucleus/common/common-util/osgi.bundle
@@ -46,10 +46,11 @@
com.sun.enterprise.util.zip; \
com.sun.logging; \
org.glassfish.admin.payload; \
- org.glassfish.common.util.admin; \
org.glassfish.common.util; \
- org.glassfish.quality; \
+ org.glassfish.common.util.admin; \
+ org.glassfish.common.util.io; \
org.glassfish.common.util.timer; \
+ org.glassfish.quality; \
org.glassfish.security.common; \
com.sun.logging.enterprise.system.core; \
org.glassfish.server; version=${project.osgi.version}
diff --git a/nucleus/common/common-util/src/main/java/org/glassfish/common/util/io/EmptyInputStream.java b/nucleus/common/common-util/src/main/java/org/glassfish/common/util/io/EmptyInputStream.java
new file mode 100644
index 0000000..7289af9
--- /dev/null
+++ b/nucleus/common/common-util/src/main/java/org/glassfish/common/util/io/EmptyInputStream.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2022 Eclipse Foundation and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.common.util.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Input stream which doesn't contain anything.
+ *
+ * @author David Matejcek
+ */
+public class EmptyInputStream extends InputStream {
+
+ @Override
+ public int read() throws IOException {
+ return -1;
+ }
+
+
+ @Override
+ public int available() throws IOException {
+ return 0;
+ }
+
+
+ @Override
+ public int read(final byte[] b) throws IOException {
+ return -1;
+ }
+
+
+ @Override
+ public int read(final byte[] b, final int off, final int len) throws IOException {
+ return -1;
+ }
+}
diff --git a/nucleus/common/common-util/src/main/java/org/glassfish/common/util/io/EmptyOutputStream.java b/nucleus/common/common-util/src/main/java/org/glassfish/common/util/io/EmptyOutputStream.java
new file mode 100644
index 0000000..5f3e07e
--- /dev/null
+++ b/nucleus/common/common-util/src/main/java/org/glassfish/common/util/io/EmptyOutputStream.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2022 Eclipse Foundation and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.common.util.io;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+
+/**
+ * Ignores all input, so is always empty.
+ *
+ * @author David Matejcek
+ */
+public class EmptyOutputStream extends OutputStream {
+
+ @Override
+ public void write(int b) throws IOException {
+ return;
+ }
+
+
+ @Override
+ public void write(byte[] b) throws IOException {
+ return;
+ }
+
+
+ @Override
+ public void write(byte[] b, int off, int len) throws IOException {
+ return;
+ }
+}
diff --git a/nucleus/osgi-platforms/osgi-cli-interactive/src/main/java/org/glassfish/osgi/cli/interactive/LocalOSGiShellCommand.java b/nucleus/osgi-platforms/osgi-cli-interactive/src/main/java/org/glassfish/osgi/cli/interactive/LocalOSGiShellCommand.java
index 2830c98..15491a7 100644
--- a/nucleus/osgi-platforms/osgi-cli-interactive/src/main/java/org/glassfish/osgi/cli/interactive/LocalOSGiShellCommand.java
+++ b/nucleus/osgi-platforms/osgi-cli-interactive/src/main/java/org/glassfish/osgi/cli/interactive/LocalOSGiShellCommand.java
@@ -18,13 +18,14 @@
package org.glassfish.osgi.cli.interactive;
import com.sun.enterprise.admin.cli.ArgumentTokenizer;
+import com.sun.enterprise.admin.cli.ArgumentTokenizer.ArgumentException;
import com.sun.enterprise.admin.cli.CLICommand;
import com.sun.enterprise.admin.cli.CLIUtil;
import com.sun.enterprise.admin.cli.Environment;
import com.sun.enterprise.admin.cli.MultimodeCommand;
import com.sun.enterprise.admin.cli.ProgramOptions;
import com.sun.enterprise.admin.cli.remote.RemoteCLICommand;
-import com.sun.enterprise.admin.util.CommandModelData;
+import com.sun.enterprise.admin.util.CommandModelData.ParamModelData;
import com.sun.enterprise.universal.i18n.LocalStringsImpl;
import jakarta.inject.Inject;
@@ -33,26 +34,28 @@
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.IOException;
-import java.io.OutputStream;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
+import java.util.logging.Level;
import org.glassfish.api.I18n;
import org.glassfish.api.Param;
import org.glassfish.api.admin.CommandException;
import org.glassfish.api.admin.CommandModel.ParamModel;
import org.glassfish.api.admin.CommandValidationException;
-import org.glassfish.api.admin.InvalidCommandException;
+import org.glassfish.common.util.io.EmptyOutputStream;
import org.glassfish.hk2.api.ActiveDescriptor;
import org.glassfish.hk2.api.DynamicConfiguration;
import org.glassfish.hk2.api.DynamicConfigurationService;
+import org.glassfish.hk2.api.MultiException;
import org.glassfish.hk2.api.PerLookup;
import org.glassfish.hk2.api.ServiceLocator;
-import org.glassfish.hk2.utilities.BuilderHelper;
import org.jline.reader.Completer;
+import org.jline.reader.EndOfFileException;
import org.jline.reader.LineReader;
import org.jline.reader.LineReaderBuilder;
import org.jline.reader.impl.completer.NullCompleter;
@@ -61,6 +64,9 @@
import org.jline.terminal.TerminalBuilder;
import org.jvnet.hk2.annotations.Service;
+import static org.glassfish.hk2.utilities.BuilderHelper.createConstantDescriptor;
+import static org.glassfish.hk2.utilities.BuilderHelper.createContractFilter;
+
/**
* A simple local asadmin sub-command to establish an interactive osgi shell.
*
@@ -76,12 +82,12 @@
@PerLookup
public class LocalOSGiShellCommand extends CLICommand {
- protected static final String REMOTE_COMMAND = "osgi";
- protected static final String SESSIONID_OPTION = "--session-id";
- protected static final String SESSION_OPTION = "--session";
- protected static final String SESSION_OPTION_EXECUTE = "execute";
- protected static final String SESSION_OPTION_START = "new";
- protected static final String SESSION_OPTION_STOP = "stop";
+ private static final String REMOTE_COMMAND = "osgi";
+ private static final String SESSIONID_OPTION = "--session-id";
+ private static final String SESSION_OPTION = "--session";
+ private static final String SESSION_OPTION_EXECUTE = "execute";
+ private static final String SESSION_OPTION_START = "new";
+ private static final String SESSION_OPTION_STOP = "stop";
private static final LocalStringsImpl STRINGS = new LocalStringsImpl(MultimodeCommand.class);
@Inject
@@ -104,58 +110,22 @@
private String shellType;
- protected String[] enhanceForTarget(String[] args) {
- if (instance == null) {
- return null;
+ @Override
+ public void postConstruct() {
+ super.postConstruct();
+ try {
+ cmd = new RemoteCLICommand(REMOTE_COMMAND, locator.<ProgramOptions> getService(ProgramOptions.class),
+ locator.<Environment> getService(Environment.class));
+ } catch (MultiException | CommandException e) {
+ logger.log(Level.SEVERE, "postConstruct failed!", e);
}
- String[] targetArgs = new String[args.length + 2];
- targetArgs[1] = "--instance";
- targetArgs[2] = instance;
- System.arraycopy(args, 0, targetArgs, 0, 1);
- System.arraycopy(args, 1, targetArgs, 3, args.length - 1);
- return targetArgs;
}
- protected String[] prepareArguments(String sessionId, String[] args) {
- if (sessionId == null) {
- String[] osgiArgs = new String[args.length + 1];
- osgiArgs[0] = REMOTE_COMMAND;
- System.arraycopy(args, 0, osgiArgs, 1, args.length);
- args = osgiArgs;
- } else {
- // attach command to remote session...
- String[] sessionArgs = new String[args.length + 5];
- sessionArgs[0] = REMOTE_COMMAND;
- sessionArgs[1] = SESSION_OPTION;
- sessionArgs[2] = SESSION_OPTION_EXECUTE;
- sessionArgs[3] = SESSIONID_OPTION;
- sessionArgs[4] = sessionId;
- System.arraycopy(args, 0, sessionArgs, 5, args.length);
- args = sessionArgs;
- }
- return args;
- }
-
- protected String startSession() throws CommandException {
- if (!"gogo".equals(shellType)) {
- return null;
- }
- String[] args = {REMOTE_COMMAND, SESSION_OPTION, SESSION_OPTION_START};
- return cmd.executeAndReturnOutput(enhanceForTarget(args)).trim();
- }
-
- protected int stopSession(String sessionId) throws CommandException {
- if (sessionId == null) {
- return 0;
- }
- String[] args = {REMOTE_COMMAND, SESSION_OPTION, SESSION_OPTION_STOP, SESSIONID_OPTION, sessionId};
- return cmd.execute(enhanceForTarget(args));
- }
/**
* The validate method validates that the type and quantity of
* parameters and operands matches the requirements for this
- * command. The validate method supplies missing options from
+ * command. The validate method supplies missing options from
* the environment.
*/
@Override
@@ -174,9 +144,8 @@
protected Collection<ParamModel> usageOptions() {
Collection<ParamModel> opts = commandModel.getParameters();
Set<ParamModel> uopts = new LinkedHashSet<>();
- ParamModel p = new CommandModelData.ParamModelData("printprompt",
- boolean.class, true,
- Boolean.toString(programOpts.isInteractive()));
+ String interactive = Boolean.toString(programOpts.isInteractive());
+ ParamModel p = new ParamModelData("printprompt", boolean.class, true, interactive);
for (ParamModel pm : opts) {
if (pm.getName().equals("printprompt")) {
uopts.add(p);
@@ -192,49 +161,48 @@
if (cmd == null) {
throw new CommandException("Remote command 'osgi' is not available.");
}
- programOpts.setEcho(echo); // restore echo flag, saved in validate
- if (encoding != null) {
- // see Configuration.getEncoding()...
- System.setProperty("input.encoding", encoding);
- }
- final String[] args = enhanceForTarget(new String[] {REMOTE_COMMAND, "asadmin-osgi-shell"});
- shellType = cmd.executeAndReturnOutput(args).trim();
+ programOpts.setEcho(echo);
+ // restore echo flag, saved in validate
+ if (encoding != null) {
+ // see Configuration.getEncoding()...
+ System.setProperty("input.encoding", encoding);
+ }
+ final String[] args = enhanceForTarget(new String[] {REMOTE_COMMAND, "asadmin-osgi-shell"});
+ logger.log(Level.FINEST, "executeCommand: args {0}", Arrays.toString(args));
+ shellType = cmd.executeAndReturnOutput(args).trim();
try (Terminal terminal = createTerminal()) {
- LineReader reader = LineReaderBuilder.builder().completer(getCommandCompleter()).appName(REMOTE_COMMAND).terminal(terminal).build();
+ LineReader reader = LineReaderBuilder.builder().completer(getCommandCompleter()).appName(REMOTE_COMMAND)
+ .terminal(terminal).build();
return executeCommands(reader);
} catch (IOException e) {
throw new CommandException(e);
}
}
+ private String[] enhanceForTarget(String[] args) {
+ if (instance == null) {
+ return args;
+ }
+ String[] targetArgs = new String[args.length + 2];
+ targetArgs[1] = "--instance";
+ targetArgs[2] = instance;
+ System.arraycopy(args, 0, targetArgs, 0, 1);
+ System.arraycopy(args, 1, targetArgs, 3, args.length - 1);
+ return targetArgs;
+ }
+
private Terminal createTerminal() throws IOException, CommandException {
if (file == null) {
System.out.println(STRINGS.get("multimodeIntro"));
- return TerminalBuilder.builder().streams(new FileInputStream(FileDescriptor.in), System.out).build();
+ FileInputStream inputStream = new FileInputStream(FileDescriptor.in);
+ return TerminalBuilder.builder().streams(inputStream, System.out).build();
}
if (!file.canRead()) {
throw new CommandException("File: " + file + " can not be read");
}
- OutputStream out = new OutputStream() {
-
- @Override
- public void write(int b) throws IOException {
- return;
- }
-
- @Override
- public void write(byte[] b) throws IOException {
- return;
- }
-
- @Override
- public void write(byte[] b, int off, int len) throws IOException {
- return;
- }
- };
- return TerminalBuilder.builder().streams(new FileInputStream(file), out).build();
+ return TerminalBuilder.builder().streams(new FileInputStream(file), new EmptyOutputStream()).build();
}
@@ -244,7 +212,7 @@
* @return The command completer
*/
private Completer getCommandCompleter() {
- if("gogo".equals(shellType)) {
+ if ("gogo".equals(shellType)) {
return new StringsCompleter(
"bundlelevel",
"cd",
@@ -285,7 +253,7 @@
"repos",
"source"
);
- } else if("felix".equals(shellType)) {
+ } else if ("felix".equals(shellType)) {
return new StringsCompleter(
"exit",
"quit",
@@ -336,49 +304,44 @@
String sessionId = startSession();
try {
- for (;;) {
- if (isPromptPrinted()) {
- line = reader.readLine(shellType + "$ ");
- } else {
- line = reader.readLine();
+ while (true) {
+ try {
+ if (isPromptPrinted()) {
+ line = reader.readLine(shellType + "$ ");
+ } else {
+ line = reader.readLine();
+ }
+ } catch (EndOfFileException e) {
+ break;
}
- if (line == null) {
+ if (line == null || line.isBlank()) {
if (isPromptPrinted()) {
System.out.println();
}
break;
}
- if (line.trim().startsWith("#")) // ignore comment lines
- {
+ if (line.trim().startsWith("#")) {
+ // ignore comment lines
continue;
}
- String[] args = null;
+ final String[] args;
try {
args = getArgs(line);
- } catch (ArgumentTokenizer.ArgumentException ex) {
- logger.info(ex.getMessage());
+ } catch (ArgumentException ex) {
+ logger.severe(ex.getMessage());
continue;
}
- if (args.length == 0) {
- continue;
- }
-
- String command = args[0];
- if (command.trim().length() == 0) {
- continue;
- }
-
+ final String command = args[0];
// handle built-in exit and quit commands
- // XXX - care about their arguments?
- if (command.equals("exit") || command.equals("quit")) {
+ if ("exit".equals(command) || "quit".equals(command)) {
break;
}
- ProgramOptions po = null;
+ final String[] arguments = enhanceForTarget(prepareArguments(sessionId, args));
try {
/*
* Every command gets its own copy of program options
@@ -386,28 +349,21 @@
* command line options don't effect other commands.
* But all commands share the same environment.
*/
- po = new ProgramOptions(env);
+ final ProgramOptions programOptions = new ProgramOptions(env);
// copy over AsadminMain info
- po.setClassPath(programOpts.getClassPath());
- po.setClassName(programOpts.getClassName());
+ programOptions.setClassPath(programOpts.getClassPath());
+ programOptions.setClassName(programOpts.getClassName());
// remove the old one and replace it
- atomicReplace(locator, po);
+ atomicReplace(locator, programOptions);
- args = prepareArguments(sessionId, args);
-
- args = enhanceForTarget(args);
-
- String output = cmd.executeAndReturnOutput(args).trim();
- if(output != null && output.length() > 0) {
+ String output = cmd.executeAndReturnOutput(arguments).trim();
+ if (output != null && !output.isEmpty()) {
logger.info(output);
}
} catch (CommandValidationException cve) {
logger.severe(cve.getMessage());
logger.severe(cmd.getUsage());
rc = ERROR;
- } catch (InvalidCommandException ice) {
- // find closest match with local or remote commands
- logger.severe(ice.getMessage());
} catch (CommandException ce) {
logger.severe(ce.getMessage());
rc = ERROR;
@@ -416,8 +372,7 @@
// XXX - is this necessary?
atomicReplace(locator, programOpts);
}
-
- CLIUtil.writeCommandToDebugLog(name, env, args, rc);
+ CLIUtil.writeCommandToDebugLog(name, env, arguments, rc);
}
} finally {
// what if something breaks on the wire?
@@ -426,24 +381,64 @@
return rc;
}
+
+ private String[] prepareArguments(String sessionId, String[] args) {
+ if (sessionId == null) {
+ String[] osgiArgs = args == null ? new String[1] : new String[args.length + 1];
+ osgiArgs[0] = REMOTE_COMMAND;
+ if (args != null && args.length > 0) {
+ System.arraycopy(args, 0, osgiArgs, 1, args.length);
+ }
+ return osgiArgs;
+ }
+ // attach command to remote session...
+ String[] sessionArgs = args == null ? new String[5] : new String[args.length + 5];
+ sessionArgs[0] = REMOTE_COMMAND;
+ sessionArgs[1] = SESSION_OPTION;
+ sessionArgs[2] = SESSION_OPTION_EXECUTE;
+ sessionArgs[3] = SESSIONID_OPTION;
+ sessionArgs[4] = sessionId;
+ if (args != null && args.length > 0) {
+ System.arraycopy(args, 0, sessionArgs, 5, args.length);
+ }
+ return sessionArgs;
+ }
+
+
+ private String startSession() throws CommandException {
+ if (!"gogo".equals(shellType)) {
+ return null;
+ }
+ String[] args = {REMOTE_COMMAND, SESSION_OPTION, SESSION_OPTION_START};
+ return cmd.executeAndReturnOutput(enhanceForTarget(args)).trim();
+ }
+
+
+ private int stopSession(String sessionId) throws CommandException {
+ if (sessionId == null) {
+ return 0;
+ }
+ String[] args = {REMOTE_COMMAND, SESSION_OPTION, SESSION_OPTION_STOP, SESSIONID_OPTION, sessionId};
+ return cmd.execute(enhanceForTarget(args));
+ }
+
+
private boolean isPromptPrinted() {
return file == null;
}
+
private static void atomicReplace(ServiceLocator locator, ProgramOptions options) {
DynamicConfigurationService dcs = locator.getService(DynamicConfigurationService.class);
DynamicConfiguration config = dcs.createDynamicConfiguration();
-
- config.addUnbindFilter(BuilderHelper.createContractFilter(ProgramOptions.class.getName()));
- ActiveDescriptor<ProgramOptions> desc = BuilderHelper.createConstantDescriptor(
- options, null, ProgramOptions.class);
+ config.addUnbindFilter(createContractFilter(ProgramOptions.class.getName()));
+ ActiveDescriptor<ProgramOptions> desc = createConstantDescriptor(options, null, ProgramOptions.class);
config.addActiveDescriptor(desc);
-
config.commit();
}
- private String[] getArgs(String line) throws ArgumentTokenizer.ArgumentException {
+ private static String[] getArgs(String line) throws ArgumentException {
List<String> args = new ArrayList<>();
ArgumentTokenizer t = new ArgumentTokenizer(line);
while (t.hasMoreTokens()) {
@@ -451,16 +446,4 @@
}
return args.toArray(new String[args.size()]);
}
-
- @Override
- public void postConstruct() {
- super.postConstruct();
- try {
- cmd = new RemoteCLICommand(REMOTE_COMMAND,
- locator.<ProgramOptions>getService(ProgramOptions.class),
- locator.<Environment>getService(Environment.class));
- } catch (CommandException ex) {
- // ignore - will be handled by execute()
- }
- }
}
diff --git a/nucleus/osgi-platforms/osgi-cli-remote/pom.xml b/nucleus/osgi-platforms/osgi-cli-remote/pom.xml
index ce53c33..0fc1d81 100644
--- a/nucleus/osgi-platforms/osgi-cli-remote/pom.xml
+++ b/nucleus/osgi-platforms/osgi-cli-remote/pom.xml
@@ -53,15 +53,19 @@
<artifactId>admin-util</artifactId>
<version>${project.version}</version>
</dependency>
+
+ <!-- Alternatives -->
<dependency>
<groupId>org.apache.felix</groupId>
<artifactId>org.apache.felix.shell</artifactId>
<scope>provided</scope>
+ <optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.felix</groupId>
<artifactId>org.apache.felix.gogo.runtime</artifactId>
<scope>provided</scope>
+ <optional>true</optional>
</dependency>
</dependencies>
</project>
diff --git a/nucleus/osgi-platforms/osgi-cli-remote/src/main/java/org/glassfish/osgi/cli/remote/OSGiShellCommand.java b/nucleus/osgi-platforms/osgi-cli-remote/src/main/java/org/glassfish/osgi/cli/remote/OSGiShellCommand.java
index e331bf1..28d2c21 100644
--- a/nucleus/osgi-platforms/osgi-cli-remote/src/main/java/org/glassfish/osgi/cli/remote/OSGiShellCommand.java
+++ b/nucleus/osgi-platforms/osgi-cli-remote/src/main/java/org/glassfish/osgi/cli/remote/OSGiShellCommand.java
@@ -1,4 +1,5 @@
/*
+ * Copyright (c) 2022 Contributors to the Eclipse Foundation
* Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
@@ -19,21 +20,15 @@
import com.sun.enterprise.admin.remote.ServerRemoteAdminCommand;
import com.sun.enterprise.config.serverbeans.Domain;
import com.sun.enterprise.config.serverbeans.Server;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.PrintStream;
+
+import jakarta.inject.Inject;
+
+import java.util.Arrays;
import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
-import jakarta.inject.Inject;
-import org.apache.felix.service.command.CommandProcessor;
-import org.apache.felix.service.command.CommandSession;
-import org.apache.felix.service.command.Result;
-import org.apache.felix.shell.ShellService;
+import java.util.stream.Collectors;
+
import org.glassfish.api.ActionReport;
import org.glassfish.api.I18n;
import org.glassfish.api.Param;
@@ -50,11 +45,15 @@
import org.glassfish.hk2.api.PerLookup;
import org.glassfish.hk2.api.PostConstruct;
import org.glassfish.hk2.api.ServiceLocator;
+import org.glassfish.osgi.cli.remote.impl.OsgiShellService;
+import org.glassfish.osgi.cli.remote.impl.SessionOperation;
import org.jvnet.hk2.annotations.Service;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleReference;
-import org.osgi.framework.ServiceReference;
+
+import static org.glassfish.osgi.cli.remote.impl.OsgiShellService.ASADMIN_OSGI_SHELL;
+import static org.glassfish.osgi.cli.remote.impl.OsgiShellServiceProvider.detectService;
/**
* A simple AdminCommand that bridges to the Felix Shell Service.
@@ -77,15 +76,12 @@
@AccessRequired(resource="domain/osgi/shell", action="execute")
public class OSGiShellCommand implements AdminCommand, PostConstruct {
- private static final Logger log = Logger.getLogger(OSGiShellCommand.class.getPackage().getName());
-
- private static final Map<String, RemoteCommandSession> sessions =
- new ConcurrentHashMap<String, RemoteCommandSession>();
+ private static final Logger LOG = Logger.getLogger(OSGiShellCommand.class.getPackage().getName());
@Param(name = "command-line", primary = true, optional = true, multiple = true, defaultValue = "help")
private Object commandLine;
- @Param(name = "session", optional = true)
+ @Param(name = "session", optional = true, acceptableValues = "new,list,execute,stop")
private String sessionOp;
@Param(name = "session-id", optional = true)
@@ -102,234 +98,94 @@
@Inject
Domain domain;
- @Override
- public void execute(AdminCommandContext context) {
- ActionReport report = context.getActionReport();
-
- if(instance != null) {
- Server svr = domain.getServerNamed(instance);
- if(svr == null) {
- report.setMessage("No server target found for "
- + instance);
- report.setActionExitCode(ActionReport.ExitCode.FAILURE);
- return;
- }
- String host = svr.getAdminHost();
- int port = svr.getAdminPort();
-
- try {
- ServerRemoteAdminCommand remote =
- new ServerRemoteAdminCommand(
- locator,
- "osgi",
- host,
- port,
- false,
- "admin",
- "",
- log);
-
- ParameterMap params = new ParameterMap();
-
- if(commandLine == null) {
- params.set("DEFAULT".toLowerCase(Locale.US), "asadmin-osgi-shell");
- } else if(commandLine instanceof String) {
- params.set("DEFAULT".toLowerCase(Locale.US), (String) commandLine);
- } else if(commandLine instanceof List) {
- params.set("DEFAULT".toLowerCase(Locale.US), (List<String>) commandLine);
- }
-
- if(sessionOp != null) {
- params.set("session", sessionOp);
- }
-
- if(sessionId != null) {
- params.set("session-id", sessionId);
- }
-
- report.setMessage(remote.executeCommand(params));
- report.setActionExitCode(ActionReport.ExitCode.SUCCESS);
- return;
- } catch(CommandException x) {
- report.setMessage("Remote execution failed: "
- + x.getMessage());
- report.setFailureCause(x);
- report.setActionExitCode(ActionReport.ExitCode.FAILURE);
- return;
- }
- }
-
- String cmdName = "";
- String cmd = "";
- if(commandLine == null) {
- cmd = "asadmin-osgi-shell";
- cmdName = cmd;
- } else if(commandLine instanceof String) {
- cmd = (String) commandLine;
- cmdName = cmd;
- } else if(commandLine instanceof List) {
- for(Object arg : (List) commandLine) {
- if(cmd.length() == 0) {
- // first arg
- cmd = (String) arg;
- cmdName = cmd;
- } else {
- cmd += " " + (String) arg;
- }
- }
- } else if(commandLine instanceof String[]) {
- for(Object arg : (String[]) commandLine) {
- if(cmd.length() == 0) {
- // first arg
- cmd = (String) arg;
- cmdName = cmd;
- } else {
- cmd += " " + (String) arg;
- }
- }
- } else {
- // shouldn't happen...
- report.setMessage("Unable to deal with argument list of type "
- + commandLine.getClass().getName());
- report.setActionExitCode(ActionReport.ExitCode.WARNING);
- return;
- }
-
- // standard output...
- ByteArrayOutputStream bOut = new ByteArrayOutputStream(512);
- PrintStream out = new PrintStream(bOut);
-
- // error output...
- ByteArrayOutputStream bErr = new ByteArrayOutputStream(512);
- PrintStream err = new PrintStream(bErr);
-
- try {
- Object shell = null;
-
- ServiceReference sref = ctx.getServiceReference(
- "org.apache.felix.service.command.CommandProcessor");
- if(sref != null) {
- shell = ctx.getService(sref);
- }
-
- if(shell == null) {
- // try with felix...
- sref = ctx.getServiceReference("org.apache.felix.shell.ShellService");
- if(sref != null) {
- shell = ctx.getService(sref);
- }
-
- if(shell == null) {
- report.setMessage("No Shell Service available");
- report.setActionExitCode(ActionReport.ExitCode.WARNING);
- return;
- } else if("asadmin-osgi-shell".equals(cmdName)) {
- out.println("felix");
- } else {
- ShellService s = (ShellService) shell;
- s.executeCommand(cmd, out, err);
- }
- } else {
- // try with gogo...
-
- // GLASSFISH-19126 - prepare fake input stream...
- InputStream in = new InputStream() {
-
- @Override
- public int read() throws IOException {
- return -1;
- }
-
- @Override
- public int available() throws IOException {
- return 0;
- }
-
- @Override
- public int read(byte[] b) throws IOException {
- return -1;
- }
-
- @Override
- public int read(byte[] b, int off, int len) throws IOException {
- return -1;
- }
- };
-
- CommandProcessor cp = (CommandProcessor) shell;
- if (sessionOp == null) {
- if("asadmin-osgi-shell".equals(cmdName)) {
- out.println("gogo");
- } else {
- CommandSession session = cp.createSession(in, out, err);
- Object result = session.execute(cmd);
-
- if (result instanceof String) {
- out.println(result.toString());
- }
-
- session.close();
- }
- } else if("new".equals(sessionOp)) {
- CommandSession session = cp.createSession(in, out, err);
- RemoteCommandSession remote = new RemoteCommandSession(session);
-
- log.log(Level.FINE, "Remote session established: {0}",
- remote.getId());
-
- sessions.put(remote.getId(), remote);
- out.println(remote.getId());
- } else if("list".equals(sessionOp)) {
- for(String id : sessions.keySet()) {
- out.println(id);
- }
- } else if("execute".equals(sessionOp)) {
- RemoteCommandSession remote = sessions.get(sessionId);
- CommandSession session = remote.attach(in, out, err);
- Object result = session.execute(cmd);
-
- if (result instanceof String) {
- out.println(result.toString());
- }
-
- remote.detach();
- } else if("stop".equals(sessionOp)) {
- RemoteCommandSession remote = sessions.remove(sessionId);
- CommandSession session = remote.attach(in, out, err);
- session.close();
-
- log.log(Level.FINE, "Remote session closed: {0}",
- remote.getId());
- }
- }
-
- out.flush();
- err.flush();
-
- String output = bOut.toString("UTF-8");
- String errors = bErr.toString("UTF-8");
- report.setMessage(output);
-
- if(errors.length() > 0) {
- report.setMessage(errors);
- report.setActionExitCode(ActionReport.ExitCode.WARNING);
- } else {
- report.setActionExitCode(ActionReport.ExitCode.SUCCESS);
- }
- } catch (Exception ex) {
- report.setMessage(ex.getMessage());
- report.setActionExitCode(ActionReport.ExitCode.WARNING);
- } finally {
- out.close();
- err.close();
- }
- }
@Override
public void postConstruct() {
- if(ctx == null) {
- Bundle me = BundleReference.class.cast(getClass().getClassLoader()).getBundle();
+ if (ctx == null) {
+ final Bundle me = BundleReference.class.cast(getClass().getClassLoader()).getBundle();
ctx = me.getBundleContext();
}
}
+
+
+ @Override
+ public void execute(final AdminCommandContext context) {
+ final ActionReport report = context.getActionReport();
+
+ if (instance != null) {
+ final Server server = domain.getServerNamed(instance);
+ if (server == null) {
+ report.setMessage("No server target found for " + instance);
+ report.setActionExitCode(ActionReport.ExitCode.FAILURE);
+ return;
+ }
+ final String host = server.getAdminHost();
+ final int port = server.getAdminPort();
+ try {
+ final ServerRemoteAdminCommand remote
+ = new ServerRemoteAdminCommand(locator, "osgi", host, port, false, "admin", "", LOG);
+
+ final ParameterMap params = new ParameterMap();
+ if (commandLine == null) {
+ params.set("default", ASADMIN_OSGI_SHELL);
+ } else if (commandLine instanceof String) {
+ params.set("default", (String) commandLine);
+ } else if (commandLine instanceof List) {
+ params.set("default", (List<String>) commandLine);
+ }
+ if (sessionOp != null) {
+ params.set("session", sessionOp);
+ }
+ if (sessionId != null) {
+ params.set("session-id", sessionId);
+ }
+ report.setMessage(remote.executeCommand(params));
+ report.setActionExitCode(ActionReport.ExitCode.SUCCESS);
+ return;
+ } catch(final CommandException e) {
+ report.setMessage("Remote execution failed: " + e.getMessage());
+ report.setFailureCause(e);
+ report.setActionExitCode(ActionReport.ExitCode.FAILURE);
+ return;
+ }
+ }
+
+ // must not be null, called services forbid it.
+ final String cmdName;
+ final String cmd;
+ if (commandLine == null) {
+ cmdName = ASADMIN_OSGI_SHELL;
+ cmd = ASADMIN_OSGI_SHELL;
+ } else if (commandLine instanceof String) {
+ cmdName = (String) commandLine;
+ cmd = cmdName;
+ } else if (commandLine instanceof List) {
+ @SuppressWarnings("unchecked")
+ final List<String> list = (List<String>) commandLine;
+ cmdName = list.isEmpty() ? "" : list.get(0);
+ cmd = list.isEmpty() ? "" : list.stream().collect(Collectors.joining(" "));
+ } else if (commandLine instanceof String[]) {
+ final String[] list = (String[]) commandLine;
+ cmdName = list.length == 0 ? "" : list[0];
+ cmd = list.length == 0 ? "" : Arrays.stream(list).collect(Collectors.joining(" "));
+ } else {
+ report.setMessage("Unable to deal with argument list of type " + commandLine.getClass().getName());
+ report.setActionExitCode(ActionReport.ExitCode.FAILURE);
+ return;
+ }
+ try {
+ final SessionOperation sessionOperation = SessionOperation.parse(sessionOp);
+ final OsgiShellService service = detectService(ctx, sessionOperation, sessionId, report);
+ if (service == null) {
+ report.setMessage("No Shell Service available");
+ report.setActionExitCode(ActionReport.ExitCode.FAILURE);
+ return;
+ }
+ service.exec(cmdName, cmd);
+ } catch (final Exception ex) {
+ LOG.log(Level.SEVERE, ex.getMessage());
+ report.setMessage(ex.getMessage());
+ report.setActionExitCode(ActionReport.ExitCode.FAILURE);
+ }
+ }
}
diff --git a/nucleus/osgi-platforms/osgi-cli-remote/src/main/java/org/glassfish/osgi/cli/remote/impl/FelixOsgiShellService.java b/nucleus/osgi-platforms/osgi-cli-remote/src/main/java/org/glassfish/osgi/cli/remote/impl/FelixOsgiShellService.java
new file mode 100644
index 0000000..a2fba91
--- /dev/null
+++ b/nucleus/osgi-platforms/osgi-cli-remote/src/main/java/org/glassfish/osgi/cli/remote/impl/FelixOsgiShellService.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2022 Eclipse Foundation and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.osgi.cli.remote.impl;
+
+import org.apache.felix.shell.ShellService;
+import org.glassfish.api.ActionReport;
+
+/**
+ * Service using {@link ShellService} and NOT supporting sessions.
+ *
+ * @author David Matejcek
+ */
+class FelixOsgiShellService extends OsgiShellService {
+
+ private final ShellService service;
+
+ FelixOsgiShellService(Object service, ActionReport report) {
+ super(report);
+ this.service = (ShellService) service;
+ }
+
+
+ @Override
+ protected void execCommand(String cmdName, String cmd) throws Exception {
+ if (ASADMIN_OSGI_SHELL.equals(cmdName)) {
+ stdout.println("felix");
+ } else {
+ service.executeCommand(cmd, stdout, stderr);
+ }
+ }
+}
diff --git a/nucleus/osgi-platforms/osgi-cli-remote/src/main/java/org/glassfish/osgi/cli/remote/impl/GogoOsgiShellService.java b/nucleus/osgi-platforms/osgi-cli-remote/src/main/java/org/glassfish/osgi/cli/remote/impl/GogoOsgiShellService.java
new file mode 100644
index 0000000..8183d41
--- /dev/null
+++ b/nucleus/osgi-platforms/osgi-cli-remote/src/main/java/org/glassfish/osgi/cli/remote/impl/GogoOsgiShellService.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2022 Eclipse Foundation and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.osgi.cli.remote.impl;
+
+import java.io.InputStream;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.apache.felix.service.command.CommandProcessor;
+import org.apache.felix.service.command.CommandSession;
+import org.glassfish.api.ActionReport;
+import org.glassfish.common.util.io.EmptyInputStream;
+
+import static org.glassfish.osgi.cli.remote.impl.SessionOperation.EXECUTE;
+import static org.glassfish.osgi.cli.remote.impl.SessionOperation.LIST;
+import static org.glassfish.osgi.cli.remote.impl.SessionOperation.NEW;
+import static org.glassfish.osgi.cli.remote.impl.SessionOperation.STOP;
+
+/**
+ * Service using {@link CommandProcessor} and supporting sessions.
+ *
+ * @author David Matejcek
+ */
+class GogoOsgiShellService extends OsgiShellService {
+ private static final Logger LOG = Logger.getLogger(GogoOsgiShellService.class.getName());
+
+ private static final Map<String, RemoteCommandSession> SESSIONS = new ConcurrentHashMap<>();
+ private final CommandProcessor processor;
+ private final SessionOperation sessionOperation;
+ private final String sessionId;
+
+ GogoOsgiShellService(Object service, SessionOperation sessionOperation, String sessionId, ActionReport report) {
+ super(report);
+ this.processor = (CommandProcessor) service;
+ this.sessionOperation = sessionOperation;
+ this.sessionId = sessionId;
+ }
+
+ @Override
+ protected void execCommand(String cmdName, String cmd) throws Exception {
+ final InputStream in = new EmptyInputStream();
+ if (sessionOperation == null) {
+ if (ASADMIN_OSGI_SHELL.equals(cmdName)) {
+ stdout.println("gogo");
+ } else {
+ try (CommandSession session = processor.createSession(in, stdout, stderr)) {
+ final Object result = session.execute(cmd);
+ if (result instanceof String) {
+ stdout.println(result.toString());
+ }
+ }
+ }
+ } else if (sessionOperation == NEW) {
+ final CommandSession session = processor.createSession(in, stdout, stderr);
+ final RemoteCommandSession remote = new RemoteCommandSession(session);
+ LOG.log(Level.FINE, "Remote session established: {0}", remote.getId());
+ SESSIONS.put(remote.getId(), remote);
+ stdout.println(remote.getId());
+ } else if (sessionOperation == LIST) {
+ for (final String id : SESSIONS.keySet()) {
+ stdout.println(id);
+ }
+ } else if (sessionOperation == EXECUTE) {
+ final RemoteCommandSession remote = SESSIONS.get(sessionId);
+ final CommandSession session = remote.attach(in, stdout, stderr);
+ final Object result = session.execute(cmd);
+ if (result instanceof String) {
+ stdout.println(result.toString());
+ }
+ remote.detach();
+ } else if (sessionOperation == STOP) {
+ final RemoteCommandSession remote = SESSIONS.remove(sessionId);
+ if (remote != null) {
+ final CommandSession session = remote.attach(in, stdout, stderr);
+ session.close();
+ }
+ LOG.log(Level.FINE, "Remote session closed: {0}", sessionId);
+ }
+ }
+}
diff --git a/nucleus/osgi-platforms/osgi-cli-remote/src/main/java/org/glassfish/osgi/cli/remote/impl/OsgiShellService.java b/nucleus/osgi-platforms/osgi-cli-remote/src/main/java/org/glassfish/osgi/cli/remote/impl/OsgiShellService.java
new file mode 100644
index 0000000..76238f1
--- /dev/null
+++ b/nucleus/osgi-platforms/osgi-cli-remote/src/main/java/org/glassfish/osgi/cli/remote/impl/OsgiShellService.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2022 Eclipse Foundation and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.osgi.cli.remote.impl;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.glassfish.api.ActionReport;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+/**
+ * Common parent of OSGi Shell service wrappers.
+ *
+ * @author David Matejcek
+ */
+public abstract class OsgiShellService {
+ /** Used by LocalOSGiShellCommand */
+ public static final String ASADMIN_OSGI_SHELL = "asadmin-osgi-shell";
+ private static final Logger LOG = Logger.getLogger(OsgiShellService.class.getName());
+
+ private final ActionReport report;
+ private final ByteArrayOutputStream stdoutBytes;
+ private final ByteArrayOutputStream stderrBytes;
+ /** Can be used to send response to user */
+ protected final PrintStream stdout;
+ /** Error output for user */
+ protected final PrintStream stderr;
+
+
+ /**
+ * Initializes streams collecting output,
+ *
+ * @param report
+ */
+ protected OsgiShellService(final ActionReport report) {
+ this.report = report;
+ this.stdoutBytes = new ByteArrayOutputStream(1024);
+ this.stdout = new PrintStream(stdoutBytes);
+ this.stderrBytes = new ByteArrayOutputStream(1024);
+ this.stderr = new PrintStream(stderrBytes);
+ }
+
+
+ /**
+ * Executes the command.
+ *
+ * @param cmdName - command name. Can be {@value #ASADMIN_OSGI_SHELL} or the first command of cmd.
+ * @param cmd - command and arguments.
+ * @return updated {@link ActionReport} given in constructor
+ * @throws Exception
+ */
+ public ActionReport exec(final String cmdName, final String cmd) throws Exception {
+ LOG.log(Level.FINE, "exec: {0}", cmd);
+ execCommand(cmdName, cmd);
+ stdout.flush();
+ stderr.flush();
+ return generateReport();
+ }
+
+
+ /**
+ * Calls the real implementation of the OSGI shell.
+ *
+ * @param cmdName - command name. Can be {@value #ASADMIN_OSGI_SHELL} or the first command of cmd.
+ * @param cmd - command and arguments.
+ * @throws Exception
+ */
+ protected abstract void execCommand(String cmdName, String cmd) throws Exception;
+
+
+ private ActionReport generateReport() {
+ final String output = stdoutBytes.toString(UTF_8);
+ report.setMessage(output);
+
+ final String errors = stderrBytes.toString(UTF_8);
+ if (errors.isEmpty()) {
+ report.setActionExitCode(ActionReport.ExitCode.SUCCESS);
+ } else {
+ report.setMessage(errors);
+ report.setActionExitCode(ActionReport.ExitCode.FAILURE);
+ }
+ return report;
+ }
+}
diff --git a/nucleus/osgi-platforms/osgi-cli-remote/src/main/java/org/glassfish/osgi/cli/remote/impl/OsgiShellServiceProvider.java b/nucleus/osgi-platforms/osgi-cli-remote/src/main/java/org/glassfish/osgi/cli/remote/impl/OsgiShellServiceProvider.java
new file mode 100644
index 0000000..65e3db0
--- /dev/null
+++ b/nucleus/osgi-platforms/osgi-cli-remote/src/main/java/org/glassfish/osgi/cli/remote/impl/OsgiShellServiceProvider.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2022 Eclipse Foundation and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.osgi.cli.remote.impl;
+
+import org.glassfish.api.ActionReport;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * Detects OSGI shell impl available on the classpath and provides one of them.
+ *
+ * @author David Matejcek
+ */
+public class OsgiShellServiceProvider {
+
+
+ /**
+ * Effectively masks classes as used dependencies are optional and might not be on the classpath
+ *
+ * @param ctx - used to detect and get the service impl
+ * @param sessionId - used to attach to an existing session with this id.
+ * @param sessionOperation - used to start/attach/stop
+ * @param report
+ * @return {@link OsgiShellService} or null if there is no shell impl available.
+ */
+ public static OsgiShellService detectService(final BundleContext ctx, final SessionOperation sessionOperation,
+ final String sessionId, final ActionReport report) {
+ // warning: don't replace strings by classes, classes might not be on the classpath.
+ final ServiceReference<?> processor = ctx
+ .getServiceReference("org.apache.felix.service.command.CommandProcessor");
+ if (processor != null) {
+ return new GogoOsgiShellService(ctx.getService(processor), sessionOperation, sessionId, report);
+ }
+ final ServiceReference<?> shell = ctx.getServiceReference("org.apache.felix.shell.ShellService");
+ if (shell != null) {
+ return new FelixOsgiShellService(ctx.getService(shell), report);
+ }
+ return null;
+ }
+}
diff --git a/nucleus/osgi-platforms/osgi-cli-remote/src/main/java/org/glassfish/osgi/cli/remote/RemoteCommandSession.java b/nucleus/osgi-platforms/osgi-cli-remote/src/main/java/org/glassfish/osgi/cli/remote/impl/RemoteCommandSession.java
similarity index 80%
rename from nucleus/osgi-platforms/osgi-cli-remote/src/main/java/org/glassfish/osgi/cli/remote/RemoteCommandSession.java
rename to nucleus/osgi-platforms/osgi-cli-remote/src/main/java/org/glassfish/osgi/cli/remote/impl/RemoteCommandSession.java
index a08a4ff..1d685ae 100644
--- a/nucleus/osgi-platforms/osgi-cli-remote/src/main/java/org/glassfish/osgi/cli/remote/RemoteCommandSession.java
+++ b/nucleus/osgi-platforms/osgi-cli-remote/src/main/java/org/glassfish/osgi/cli/remote/impl/RemoteCommandSession.java
@@ -1,4 +1,5 @@
/*
+ * Copyright (c) 2022 Contributors to the Eclipse Foundation
* Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
@@ -14,7 +15,7 @@
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/
-package org.glassfish.osgi.cli.remote;
+package org.glassfish.osgi.cli.remote.impl;
import java.io.InputStream;
import java.io.PrintStream;
@@ -26,6 +27,7 @@
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.UUID;
+
import org.apache.felix.service.command.CommandSession;
/**
@@ -39,49 +41,47 @@
*
* @author ancoron
*/
-public class RemoteCommandSession {
+class RemoteCommandSession {
private final CommandSession delegate;
private final String id;
- public RemoteCommandSession(CommandSession delegate)
- {
+ public RemoteCommandSession(final CommandSession delegate) {
this.delegate = delegate;
this.id = UUID.randomUUID().toString();
}
+
/**
- * Get the identifier for this session, which is a UUID of type 4.
- *
- * @return
+ * @return the identifier for this session, which is a UUID of type 4.
*/
public String getId() {
return id;
}
+
/**
- * Attached the specified streams to the delegate of this instance and
+ * Attaches the specified streams to the delegate of this instance and
* returns the modified delegate.
*
* @param in The "stdin" stream for the session
* @param out The "stdout" stream for the session
* @param err The "stderr" stream for the session
- *
* @return The modified {@link CommandSession} delegate
- *
* @see #detach()
*/
- public CommandSession attach(InputStream in, PrintStream out, PrintStream err) {
+ public CommandSession attach(final InputStream in, final PrintStream out, final PrintStream err) {
set(this.delegate, "in", in);
set(this.delegate, "out", out);
set(this.delegate, "err", err);
- ReadableByteChannel inCh = Channels.newChannel(in);
- WritableByteChannel outCh = Channels.newChannel(out);
- WritableByteChannel errCh = out == err ? outCh : Channels.newChannel(err);
+ final ReadableByteChannel inCh = Channels.newChannel(in);
+ final WritableByteChannel outCh = Channels.newChannel(out);
+ final WritableByteChannel errCh = out == err ? outCh : Channels.newChannel(err);
set(this.delegate, "channels", new Channel[] {inCh, outCh, errCh});
return this.delegate;
}
+
/**
* Detaches all previously attached streams and hence, ensures that there
* are no stale references left.
@@ -94,11 +94,14 @@
set(this.delegate, "err", null);
}
+
private void set(final Object obj, final String field, final Object value) {
try {
final Field f = obj.getClass().getDeclaredField(field);
- final boolean accessible = f.isAccessible();
- if(!accessible) {
+ final boolean accessible = f.canAccess(obj);
+ if (accessible) {
+ f.set(obj, value);
+ } else {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
@Override
@@ -106,7 +109,7 @@
f.setAccessible(true);
try {
f.set(obj, value);
- } catch(Exception x) {
+ } catch (final Exception x) {
throw new RuntimeException(x);
}
@@ -115,10 +118,8 @@
return null;
}
});
- } else {
- f.set(obj, value);
}
- } catch(Exception x) {
+ } catch (final Exception x) {
throw new RuntimeException(x);
}
}
diff --git a/nucleus/osgi-platforms/osgi-cli-remote/src/main/java/org/glassfish/osgi/cli/remote/impl/SessionOperation.java b/nucleus/osgi-platforms/osgi-cli-remote/src/main/java/org/glassfish/osgi/cli/remote/impl/SessionOperation.java
new file mode 100644
index 0000000..b07868e
--- /dev/null
+++ b/nucleus/osgi-platforms/osgi-cli-remote/src/main/java/org/glassfish/osgi/cli/remote/impl/SessionOperation.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2022 Eclipse Foundation and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.osgi.cli.remote.impl;
+
+
+/**
+ * Commands for session manipulation.
+ *
+ * @author David Matejcek
+ */
+public enum SessionOperation {
+
+ /** Create session */
+ NEW,
+ /** List sessions */
+ LIST,
+ /** Execute command in session */
+ EXECUTE,
+ /** Stop session */
+ STOP;
+
+ /**
+ * @param operation see {@link SessionOperation} values. Case insensitive.
+ * @return {@link SessionOperation} or null if operation was null.
+ * @throws IllegalArgumentException if the operation is an unknown string.
+ *
+ */
+ public static SessionOperation parse(String operation) throws IllegalArgumentException {
+ if (operation == null) {
+ return null;
+ }
+ for (SessionOperation value : values()) {
+ if (value.name().equalsIgnoreCase(operation)) {
+ return value;
+ }
+ }
+ throw new IllegalArgumentException("Unsupported session operation: " + operation);
+ }
+
+}