I have started adapting an older part of my code base and realised I had made the mistake of overusing the System.out.printf() method. Previously the class would handle commands given by the CLI user who was operating the server, however now I am adding the capability for connecting clients to essentially become administrators (assuming they have been issued with the admin status by the initial CLI user).
In order to save rewriting a lot of my code base I figured the best idea would be to issue certain commands given by the admin clients using the same class/methods as the CLI user (So the client's command has the exact same effect as a CLI user's command and so the client may see the same output a CLI user would).
My problem is the method that I am using for the CLI user's commands over-uses the printf() method from System.out for command output. How can I adapt this class so that both CLI users and clients may obtain the same output.
Things to note:
client refers to a Socket connection of user that is connecting remotely and is using a username that is registered with the server.
The output of some of the methods contain strings that need to be given in "real-time" correspondence to the event; therefore returning the output String from the method would not be suitable in this scenario.
The following is a very rough 'pseudo' copy of the class outlining the issue. I am willing to show people the main class through a GitHub link or similar, but I did not want to initially swamp this question with code.
Code
public boolean executeCommand(String[] command) {
switch (command[0]) {
case "kill":
return kill(command);
case "clients":
if (!clientList.isEmpty())
for (String username: clientList.keySet())
System.out.printf("%s\t%s\n\n", username, clientList.get(username).getAddress());
else
System.out.println("No clients connected!");
return true;
// ...and so on
default:
System.out.printf("\"%s\": command unknown.\n Type \"help\" for a list of commands.\n", command[0]);
}
}
private boolean kill(String[] args) {
args[1].disconnect(args[2]);
System.out.printf("Killed %s with reason %s", args[1], args[2]);
}
You can create your own PrintStream and assign it to System.out before you call your legacy code. All the calls to System.out will be written instead to your stream.
Related
I am trying to use py4j to open up a gateway that I can use to pass objects from java into python. When I try to open a gateway with the py4j function launch_gateway it does not seem to properly connect to my Java class. However, when I launch my java class in the command line and then connect to it in python using JavaGateway everything works as expected. I would like to be able to use the built in method as I am sure that I am not accounting for things that have already been considered in the design of py4j, but I'm just not sure what I'm doing wrong.
Let's say I wanted to create a gateway to the class sandbox.demo.solver.UtilityReporterEntryPoint.class. In the command line I can do this by executing the following:
java -cp /Users/grr/anaconda/share/py4j/py4j0.10.4.jar: sandbox.demo.solver.UtilityReporterEntryPoint py4j.GatewayServer
This launches as expected and I can use the methods in my class from within python after connecting to the gateway. So far so good.
My understanding of the py4j documentation would lead me to believe I should do the following to launch the gateway in python:
port = launch_gateway(classpath='sandbox.demo.solver.UtilityReporterEntryPoint')
params = GatewayParameters(port=port)
gateway= JavaGateway(gateway_parameters=params)
I get no errors when executing these three lines, but when I try to access my java class methods with gateway.entry_point.someMethod() it fails with the following error:
Py4JError: An error occurred while calling t.getReport. Trace:
py4j.Py4JException: Target Object ID does not exist for this gateway :t
at py4j.Gateway.invoke(Gateway.java:277)
at py4j.commands.AbstractCommand.invokeMethod(AbstractCommand.java:132)
at py4j.commands.CallCommand.execute(CallCommand.java:79)
at py4j.GatewayConnection.run(GatewayConnection.java:214)
at java.lang.Thread.run(Thread.java:745)
Obviously something is not getting called correctly within launch_gateway or I am feeding it the wrong information.
In the py4j source code for launch_gateway you can see that given the inputs you provide and those constructed by the function, a command is constructed that eventually gets called by subprocess.Popen. So given the input passed to launch_gateway above the command passed into Popen would be:
command = ['java', '-classpath', '/Users/grr/anaconda/share/py4j/py4j0.10.4.jar:sandbox.demo.solver.UtilityReporterEntryPoint', 'py4j.GatewayServer', '0']
Passing this command to Popen returns the listening port as expected. However, connecting to this listening port still does not allow access to my class methods.
Finally, passing the command as a single string to Popen without the final argument ('0'), properly launches a gateway which again operates as expected. Having taken a glance at the Java source code for py4j.GatewayServer.class this makes no sense as the main method seems to indicate that the class should exit with status 1 if the length of arguments is 0.
At this point I'm kind of at a loss. I can hack my way into a workable solution, but as I said I'm sure that ignores important aspects of the gateway behavior and I don't like hacky solutions. I'd love to tag #Barthelemy in this one, but hopefully he reads this. Thanks in advance for any help.
EDIT
For now I have been able to work around this issue with the following steps.
Package entire project including all external dependencies into a single jar file magABM-all.jar, with 'Main-Class' set to UtilityReporterEntryPoint.
Include if...else block regarding presence of --die-on-exit exactly like it is in GatewayServer.java
Use subprocess.Popen to call the command to run the project jar.
UtilityReporterEntryPoint.java
public static void main(String[] args) throws IOException {
GatewayServer server = new GatewayServer(new UtilityReporterEntryPoint());
System.out.println("Gateway Server Started");
server.start();
if (args[0].equals("--die-on-exit")) {
try {
BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in, Charset.forName("UTF-8")));
stdin.readLine();
System.exit(0);
} catch (java.io.IOException e) {
System.exit(1);
}
}
}
app.py
def setup_gateway()
"""Launch a py4j gateway using UtilityReporterEntryPoint."""
process = subprocess.Popen('java -jar magABM-all.jar --die-on-exit', shell=True)
time.sleep(0.5)
gateway = JavaGateway()
return gateway
In this way I can still use gateway.shutdown if necessary and if the python process that starts the py4j gateway dies or is closed the gateway will be closed.
N.B I would by no means consider this a final solution as py4j was written by much smarter individuals with a clear purpose in mind and I am sure that there is a way to manage this exact workflow within the confines of py4j. This is just a stopgap solution.
There are a few issues:
The classpath parameter in launch_gateway should be a directory or a jar file, not a class name. For example, if you want to include additional Java libraries, you would add them to the classpath parameter.
The error you receive when you call gateway.entry_point.someMethod() means that you have no entry point. When you call launch_gateway, the JVM is started with GatewayServer.main, which launches a GatewayServer with no entry point: GatewayServer server = new GatewayServer(null, port). It is not possible currently to use launch_gateway and specify an entry point.
When you start the JVM with java -cp /Users/grr/anaconda/share/py4j/py4j0.10.4.jar: sandbox.demo.solver.UtilityReporterEntryPoint py4j.GatewayServer I believe the JVM uses UtilityReporterEntryPoint as the main class. Although you did not provide the code, I assume that this class has a main method and that it launches a GatewayServer with an instance of UtilityReporterEntryPoint as the entry point. Note that there is a whitespace between the colon and the class name so UtilityReporterEntryPoint is seen as the main class and not as being part of the classpath.
Note: this question isn't necessary just for Android developers. It should be the same for Java developers that performed root operations on Linux too.
Background
My app allows the user the perform various root operations (an operation that can only be performed on rooted devices).
Until now, each time the app wishes to perform a root operation, I had opened a new process using the Runtime.getRuntime().exec("su") command.
I wish to improve the way the app works, by calling this command only once, and then writing to the outputStream and reading from the inputStream while doing so.
A similar app that does this is "Terminal emulator". Once you type there "su" (and grant it root permission), it will allow you go perform any root operation and read any path of the device.
The problem
It seems that only the "echo" command works, but any other command doesn't. Also, I couldn't think of a good way to separate between the root operations output.
What I've tried
First, in order to initialize the root batching operations, I did this:
process=Runtime.getRuntime().exec("su");
_inputStreamReader=new BufferedReader(new InputStreamReader(process.getInputStream()));
_out=process.getOutputStream();
final String testLine=Long.toString(System.currentTimeMillis());
_out.write(("echo "+testLine+"\n").getBytes());
_out.flush();
final String resultLine=_inputStreamReader.readLine();
if(resultLine==null)
{
_out.write("exit \n".getBytes());
_out.flush();
_out.close();
_out=null;
_inputStreamReader.close();
_inputStreamReader=null;
process.waitFor();
return false;
}
while(!resultLine.equals(testLine))
resultLine=_inputStreamReader.readLine();
return true;
the code above (is supposed to) ensures I have root and that I can continue working on the "_out" and "inputStreamReader" . the call to "echo" has 2 purposes : to have some fake operation being done, and to also become a starting point and a separator between commands.
Here the second purpose is fake, but for each operation that I make further, this is how I thought of handling the separation of various root operations.
Now, if I want to make another root operation (assuming the above works fine), this is an example of an operation I wish to perform :
_out.write(("find "+someFolderPath+"/* -type f\n").getBytes());
_out.flush();
final String line=_inputStreamReader.readLine();
Thing is, it gets stuck on the "readLine" part, even though it should return a list of all of the file/folders within the specified path.
This command works perfectly using the previous way I performed root operations, and it should work here too.
using "echo" before those lines and then using "readLine()" shows that it's still available, as I will still get the result line. However, trying to call "readLine()" again (after the above command) will still make it stuck.
I've also tried adding "su" (or "su -" or "su -c") before the "find" command, but it didn't help.
This is the old code, which works fine:
final Process p=Runtime.getRuntime().exec("su");
final InputStream inputStream=p.getInputStream();
final DataOutputStream os=new DataOutputStream(p.getOutputStream());
os.writeBytes("find "+someFolderPath+"/* -type f\n");
os.writeBytes("exit\n");
os.flush();
//read from the inputstream (using BufferedReader) as much as I wish, till it ends.
The question
How should I avoid re-creating new processes for root operations? Is this a good way to do it?
How can I fix the above code? What is wrong with it?
Also, Is there a good way to run multiple root operations using a single "su" command, yet without any kind of separator like the one I've used (so that I could differentiate when a new command output starts and the previous one ends).
I am sure, that your find command is just too slow. You should run it in the different process.
There is great library to do this. And a manual.
OK, even though it's not quite what I wanted, and even though I wanted to learn how to do it myself, I've found that "libsuperuser" library (by "ChainFire") does it much easier.
What I've asked for is called "interactive mode" in this library.
Here's a sample class I've made that helps you do it using this library:
public class Root
{
private static final Root _instance =new Root();
private Boolean _hasRoot =null;
private Shell.Interactive _rootSession;
public interface IGotRootListener
{
public void onGotRootResult(boolean hasRoot);
}
public static Root getInstance()
{
return _instance;
}
public boolean hasRoot()
{
return _hasRoot!=null&&_hasRoot;
}
//should be called on the UI thread
public void getRoot(final IGotRootListener listener)
{
if(_hasRoot!=null&&_hasRoot)
{
listener.onGotRootResult(true);
return;
}
final AtomicReference<Interactive> rootSessionRef=new AtomicReference<>();
rootSessionRef.set(new Shell.Builder().useSU().setWantSTDERR(true).setWatchdogTimeout(5).setMinimalLogging(true).open(new Shell.OnCommandResultListener()
{
#Override
public void onCommandResult(final int commandCode,final int exitCode,final List<String> output)
{
final boolean success=exitCode==Shell.OnCommandResultListener.SHELL_RUNNING;
if(success)
_rootSession=rootSessionRef.get();
_hasRoot=success;
listener.onGotRootResult(success);
}
}));
}
... // TODO add functions to call commands via _rootSession.addCommand , only if got root
Still, if anyone has a solution to my original question, I would love to know about it.
I have found a link that illustrates the use of getting pwd based in the uid of the user.
I have a similar requirement in java for running a script as a different user which needs this implemetation.
The code snippet in c++ is as below:
static void su(const char* user)
{
struct passwd* pwentry=getpwnam(user);
if(!pwentry)
COUT<<"su:getpwnam:couldnot get pwd entry for user %s",user;
uid_t new_uid=pwentry->pw_uid;
struct passwd* pwentry_nmsadm=getpwnam("nmsadm");
if(!pwentry_nmsadm)
cout<<"su:getpwnam:could not get pwd for nmsadm");
gid_t new_gid=pwentry->pw_gid;
if(chdir(pwentry->pw_dir)<0)
cout<<"su:chdir";
uid_t current_uid=geteuid();
gid_t current_gid=getegid();
if(current_gid!=new_gid)
{
if(setgid(new_gid)<0)
cout<<"su:setgid";
}
if(current_uid!=new_uid)
{
if(setuid(new_uid)<0)
cout<<"su: setuid";
}
Please suggest some links that can be helpful(libraries that can be used) or solution to the above requirement in java.
Java does not provide this out-of-the-box, since it is both system dependant and would break security concepts of Java.
Some possible solutions could be:
Use a JNI wrapper to call your C++ method, so that you can do everything you need in C++
Use ssh to launch the script as a different user
Use sudo to launch the script as a different user
See also
Running UNIX commands as different user, from Java
How can I create a new process with another User Account on Windows? (Windows specific, but one answer mentions the JNI approach)
I want to implement the expect "interact" command using java. In expect, it's possible to open an ssh session, authenticate and, then, use the "interact" command to give the control back to the user. Is that possible with java? I've tried with expectJ, expect4J and expectForJava but there's little documentation and almost no examples of how to do this. TIA.
Update: for "interact" command reference, please check this out: http://wiki.tcl.tk/3914
"Interact is an Expect command which gives control of the current
process to the user, so that keystrokes are sent to the current
process, and the stdout and stderr of the current process are
returned."
In case anyone is interested, I have added basic interactive loop support to ExpectIt, my own open source Expect for Java implementation (sorry for self-promotion), since version 0.8.
Here is an example of interacting with the system input stream in Java 8:
try (final Expect expect = new ExpectBuilder()
.withInputs(System.in)
.build()) {
expect.interact()
.when(contains("abc")).then(r -> System.out.println("A"))
.when(contains("xyz")).then(r -> System.err.println("B"))
.until(contains("exit"));
System.out.println("DONE!");
}
System.in.close();
These libraries might suit your needs better:
SSHJ
https://github.com/shikhar/sshj
JSCH
http://www.jcraft.com/jsch/
We have a project that uses HP Quality Center and one of the regular issues we face is people not updating comments on the defect.
So I was thinkingif we could come up with a small script or tool that could be used to periodically throw up a reminder and force the user to update the comments.
I came across the Open Test Architecture API and was wondering if there are any good Python or java examples for the same that I could see.
Thanks
Hari
Example of using Python (win32com) to connect to HP Quality Center via OTA
HP Quality Center exposes a com based API called OTA.
Documentation on this is downloadable from an QC server
(OTA_API_Reference.chm) (Weirdly it is very hard to find online)
The documentation uses VBScript (The officially supported internal language for QC)
and you will need to mentally translate to Python. THis is usually very simple, but
a couple of gotchas exist.
You will need to install on your machine the Quality Center local code, this is on your windows PC
if you have been able to get to QC through the web interface.
You will also need to know the URL of the server and you username and password and the domain
of the QC project you are working on.
from win32com.client import Dispatch
conn = get_QCConnection()
for bug in get_bugs(qcConn):
print bug.Title
put_QCConnection(conn)
#below code needs to be in seperate module or at least above the fold but here
# for clarity
def get_QCConnection():
'''Get the hardcoded connection to the server and domain.
Can be made a "real" engine if you try hard.
Use makepy utility to determine if the version number has changed (TDApiOle80)
but this works to current version'''
QCConnection = Dispatch("TDApiOle80.TDConnection")
url = "http://qc.example.com/qcbin"
QCConnection.InitConnectionEx(url)
QCConnection.login("USER", "PASS")
QCConnection.Connect("google_projects", "Google_Chrome")
return QCConnection
def put_QCConnection(qcConn):
#If one person logged in to QC changes *anything* on a bug,
# they hold a global lock on writing to that bug till
# thier session times out, so really really remember to logout
# its painful to wait for your own session to time out
qcConn.Logout()
def get_bugs(qcConn):
'''just following boiler plate from vbscript
PS the SetFilter is not in QTA API, it uses Filter.
But due to the workarounds in
the very brilliant pythoncom code it supplies a virtual wrapper class
called SetFilter - this is one of those gotchas '''
BugFactory = qcConn.BugFactory
BugFilter = BugFactory.Filter
BugFilter.SetFilter(u"Status", "New")
#NB - a lot of fields in QC are malleable - and vary from site to site.
#COntact your admins for a real list of fields you can adjust
buglist = BugFilter.NewList()
return buglist
This is not a bad basis for going forward, however I create a dummy class for defects and run something like:
dfcts = [defect(b) for b in buglist]
Then I can put worker code into defect class and keep things neater.
One thing you want to do is keep access to the raw qc bug internal to the python wrapper class.
Information for others who may view this thread.
To start all this You will need install pywin32, like from here http://sourceforge.net/projects/pywin32/files/pywin32/Build216/
First of all You will need to import pywin32
'''#author: www.qcintegration.com #mailto:contact#qcintegration.com'''
import pywintypes
import win32com.client as w32c
from win32com.client import gencache, DispatchWithEvents, constants
Then as second operation I include here action on login to server
def connect_server(qc, server):
'''Connect to QC server
input = str(http adress)
output = bool(connected) TRUE/FALSE '''
try:
qc.InitConnectionEx(server);
except:
text = "Unable connect to Quality Center database: '%s'"%(server);
return qc.Connected;
def connect_login(qc, username, password):
'''Login to QC server
input = str(UserName), str(Password)
output = bool(Logged) TRUE/FALSE '''
try:
qc.Login(username, password);
except pywintypes.com_error, err:
text = unicode(err[2][2]);
return qc.LoggedIn;
def connect_project(qc, domainname, projectname):
'''Connect to Project in QC server
input = str(DomainName), str(ProjectName)
output = bool(ProjectConnected) TRUE/FALSE '''
try:
qc.Connect(domainname, projectname)
except pywintypes.com_error, err:
text = "Repository of project '%s' in domain '%s' doesn't exist or is not accessible. Please contact your Site Administrator"%(projectname, domainname);
return qc.ProjectConnected;
Second of all method which will include OTAapi dll file
def qc_instance():
'''Create QualityServer instance under variable qc
input = None
output = bool(True/False)'''
qc= None;
try:
qc = w32c.Dispatch("TDApiole80.TDConnection");
text = "DLL QualityCenter file correctly Dispatched"
return True, qc;
except:
return False, qc;
Then main method to connect to QCserver
def qcConnect(server, username, password, domainname, projectname):
print("Getting QC running files");
status, qc = qc_instance();
if status:
print("Connecting to QC server");
if connect_server(qc, server):
##connected to server
print("Checking username and password");
if connect_login(qc, username, password):
print("Connecting to QC domain and project");
if connect_project(qc, domainname, projectname):
text = "Connected"
connected = True;
return connected, text;
else:
text = "Not connected to Project in QC server.\nPlease, correct DomainName and/or ProjectName";
connected = False;
return connected, text;
else:
text = "Not logged to QC server.\nPlease, correct UserName and/or Password";
connected = False;
return connected, text;
else:
text = "Not connected to QC server.\nPlease, correct server http address";
connected = False;
return connected, text;
else:
connected = False;
text = "Unable to find QualityCenter installation files.\nPlease connect first to QualityCenter by web page to install needed files"
return connected, text;
And at the end how to execute all of those methods in one place with example of use
if __name__ == "__main__":
server= r"http://qualitycenterServer:8080/qcbin"
username= "alex_qc"
password= ""
domainname= "DEFAULT"
projectname= "QualityCenter_Demo"
connection_status, text = qcConnect(server, username, password, domainname, projectname);
print "connection_status:", connection_status
In case of any more question mailto: contact#qcintegration.com
or directly to web side: http://www.qcintegration.com
I'm not sure there are any good samples for Java, because OTA can't be consumed by Java directly, it needs a Java to COM bridnge like JIntegra.
About Python, well you can use Python COM api's. And then any OTA example will do. You got plenty in QC documentation of OTA.
But I think the real question here is, why would you want to do it in Python or Java. Why not write what you need directly in QC using it's Workflow feature. Which will allow you to write your logic in VBScript, and have it invoked inside QC UI on user actions. For instance you can bind to the Post event of a Defect / Bug and check if there is a comment and if there is not prompt the user directly with a message.
There is a REST API to HPQC (ALM11 and newer) if you want to access it from Linux without running a Windows COM component.
Here is an example that pulls in a "requirement" record (# 1202) after authenticating.
import requests
session = requests.session()
user='hpqc'
password='xxxxx'
r = session.get("http://hpqc-server:8080/qcbin/authentication-point/authenticate",auth=(user,password))
r = session.get("http://hpqc-server:8080/qcbin/rest/domains/Foo/projects/Bar/requirements/1202")
print(r.text)
The parsing of r.text from XML is left as an exercise.
Though you have asked for a Python or Java based solution, sharing the following VBA code that you can use insde HPQC/ALM's script editor (Defects module script) to accomplish the goal.
Function Bug_FieldCanChange(FieldName, NewValue)
On Error Resume Next
if not changed then
strCommentBeforeUpdate = Bug_Fields("BG_DEV_COMMENTS").Value
end if
If FieldName = "BG_DEV_COMMENTS" and blnAddCommentClicked = False Then
Msgbox "Cannot update the comments." & Chr(13)& "Changes made will not be saved."&Chr(13)& "Please use 'Add comment' button to insert new comment." &Chr(13)& " Or click Cancel without saving."
blnUpdateCommentError = true
blnAddCommentClicked = False
changed = true
End If
Bug_FieldCanChange = DefaultRes
End Function
You can use a new Test and select type (VPXP_API) which allow script to run. The good thing there is that you'd have the function definition ready to be dragged from within QC instead of having to heavily rely on doc.
I've done an implementation in Python running some script from within QC still using its API but via a QC test which is handy to retrieve directly the result (Output) etc.. going through some shell command which can then call any script on any server etc...