Spring Shell - capturing user input in middle of executing ShellMethod - spring-shell

Is the a way to capture user input in middle of executing #ShellMethod. Basically stoping executing of the method to ask for the user input and carrying on after capturing it.

There is possible solution here: https://stackoverflow.com/a/50954716, authored by ZachOfAllTrades
It works only when your app is SpringBoot-based, so you'll have access to the LineReader object, configured by SpringBoot.
#Autowired
LineReader reader;
public String ask(String question) {
return this.reader.readLine("\n" + question + " > ");
}
#ShellMethod(key = { "setService", "select" }, value = "Choose a Speech to Text Service")
public void setService() {
boolean success = false;
do {
String question = "Please select a service.";
// Get Input
String input = this.ask(question);
// Input handling
/*
* do something with input variable
*/
success = true;
}
} while (!success);
}
I didn't try it myself, though.

Use Spring Shell UI Components, now that we're in the future.
"Starting from 2.1.x there is a new component model which provides easier way to create higher level user interaction for usual use cases like asking input in a various forms. These usually are just plain text input or choosing something from a list."
#ShellComponent
public class ComponentCommands extends AbstractShellComponent {
#ShellMethod(key = "component string", value = "String input", group = "Components")
public String stringInput(boolean mask) {
StringInput component = new StringInput(getTerminal(), "Enter value", "myvalue");
component.setResourceLoader(getResourceLoader());
component.setTemplateExecutor(getTemplateExecutor());
if (mask) {
component.setMaskCharater('*');
}
StringInputContext context = component.run(StringInputContext.empty());
return "Got value " + context.getResultValue();
}
}
https://docs.spring.io/spring-shell/docs/2.1.0-SNAPSHOT/site/reference/htmlsingle/#_build_in_components

You should be able to interact directly with System.in although it is not really what Spring Shell is about: commands should be self contained.

Related

How to fetch all the fields from ServiceFeatureTable in arcgis

I am using arcgis library 100.0.0 in android for displaying maps and information inside map.
I am using the following code to populate a ServiceFeaturetable using URL provided by arcGis. I am able to load the feature layer successfully into the mapview. I have written code that listens to the click on the symbol on the map, so that I can get some information about the specific feature on the map. I am able to get the specific feature OnClick.
Upon investigating the GetAttributes() result of the specific feature, I realize that it is not having all the fields. After investigating on internet, I found that the FeatureTable.QueryFeaturesAsync could be used to get all the fields of the feature. Even though I have written the code to get all the fields, I do not know how I link this result with the feature layer, so that the feature has all the fields that I require. Here is the code
final ServiceFeatureTable serviceFeatureTable = new ServiceFeatureTable("some URL");
ListenableFuture<FeatureQueryResult> queryresult = serviceFeatureTable.queryFeaturesAsync(null, ServiceFeatureTable.QueryFeatureFields.LOAD_ALL);
// create the feature layer using the service feature table
final FeatureLayer featureLayer = new FeatureLayer(serviceFeatureTable);
featureLayer.setSelectionColor(Color.YELLOW);
featureLayer.setSelectionWidth(10);
// add the layer to the map
mapView.getMap().getOperationalLayers().add(featureLayer);
// set an on touch listener to listen for click events
mapView.setOnTouchListener(new DefaultMapViewOnTouchListener(getContext(), mapView) {
#Override
public boolean onSingleTapConfirmed(MotionEvent e) {
// get the point that was clicked and convert it to a point in map coordinates
Point clickPoint = mMapView.screenToLocation(new android.graphics.Point(Math.round(e.getX()), Math.round(e.getY())));
int tolerance = 10;
double mapTolerance = tolerance * mMapView.getUnitsPerDensityIndependentPixel();
// create objects required to do a selection with a query
Envelope envelope = new Envelope(clickPoint.getX() - mapTolerance, clickPoint.getY() - mapTolerance, clickPoint.getX() + mapTolerance, clickPoint.getY() + mapTolerance, mapView.getMap().getSpatialReference());
QueryParameters query = new QueryParameters();
query.setGeometry(envelope);
// call select features
final ListenableFuture<FeatureQueryResult> future = featureLayer.selectFeaturesAsync(query, FeatureLayer.SelectionMode.NEW);
// add done loading listener to fire when the selection returns
future.addDoneListener(new Runnable() {
#Override
public void run() {
try {
//call get on the future to get the result
FeatureQueryResult result = future.get();
// create an Iterator
Iterator<Feature> iterator = result.iterator();
Feature feature;
// cycle through selections
int counter = 0;
while (iterator.hasNext()){
feature = iterator.next();
counter++;
String name = feature.getAttributes().get(Constants.FIELD_NAME).toString();
Log.d(getResources().getString(R.string.app_name), "Selection #: " + counter + " Table name: " + feature.getFeatureTable().getTableName());
}
//Toast.makeText(getApplicationContext(), counter + " features selected", Toast.LENGTH_SHORT).show();
} catch (Exception e) {
Log.e(getResources().getString(R.string.app_name), "Select feature failed: " + e.getMessage());
}
}
});
return super.onSingleTapConfirmed(e);
}
});
Try replacing your code
from
final ListenableFuture<FeatureQueryResult> future = featureLayer.selectFeaturesAsync(query, FeatureLayer.SelectionMode.NEW);
to
final ListenableFuture<FeatureQueryResult> future = serviceFeatureTable.queryFeaturesAsync(query, ServiceFeatureTable.QueryFeatureFields.LOAD_ALL);
It works for me!

ETrade API unattended authentication

Background
The ETrade authentication system has me creating a RequestToken, then executing an Authorization URL, which opens an ETrade page.
The user logs in to authorize the activity on their account.
They receive a pin, which they enter in my app.
I call ExchangeRequestTokenForAccessToken with the RequestToken and the Pin.
Then we are off and running.
Question
The problem is I'm creating a service that runs continuously in the background. There won't be any user to log in. Conversely, I won't be making any trades. Just crunching numbers, looking for stocks that meet certain criteria.
I can't figure how to get this to work unattended.
Thanks, Brad.
Previously, I have used a series of WebRequests and manually added headers to simulate the authorization pages. This worked until about a year ago when ETrade complicated their headers with something that appears to be tracking information. I now use http://watin.org/ to log in, and to strip the Auth Code.
Sloppy code looks like this:
using WatiN.Core; // IE Automation
...
// verify current thread in STA.
Settings.Instance.MakeNewIeInstanceVisible = false;
var ieStaticInstanceHelper = new IEStaticInstanceHelper();
Settings.AutoStartDialogWatcher = false;
using (ieStaticInstanceHelper.IE = new IE())
{
string authCode = "";
ieStaticInstanceHelper.IE.GoTo(GetAuthorizationLink());
if (ieStaticInstanceHelper.IE.ContainsText("Scheduled System Maintenance"))
{
throw new ApplicationException("eTrade down for maintenance.");
}
TextField user = ieStaticInstanceHelper.IE.TextField(Find.ByName("USER"));
TextField pass = ieStaticInstanceHelper.IE.TextField(Find.ById("txtPassword"));
TextField pass2 = ieStaticInstanceHelper.IE.TextField(Find.ByName("PASSWORD"));
Button btn = ieStaticInstanceHelper.IE.Button(Find.ByClass("log-on-btn"));
Button btnAccept = ieStaticInstanceHelper.IE.Button(Find.ByValue("Accept"));
TextField authCodeBox = ieStaticInstanceHelper.IE.TextField(Find.First());
if (user != null && pass != null && btn != null &&
user.Exists && pass2.Exists && btn.Exists)
{
user.Value = username;
pass2.Value = password;
btn.Click();
}
btnAccept.WaitUntilExists(30);
btnAccept.Click();
authCodeBox.WaitUntilExists(30);
authCode = authCodeBox.Value;
SavePin(authCode);
}
Current version of Brad Melton's code.
WatiN has changed and no longer contains the IE.AttachToIE function.
So, IEStaticInstanceHelper is now called StaticBrowserInstanceHelper, but that code is hard to find, so I've included it here.
class StaticBrowserInstanceHelper<T> where T : Browser {
private Browser _browser;
private int _browserThread;
private string _browserHwnd;
public Browser Browser {
get {
int currentThreadId = GetCurrentThreadId();
if (currentThreadId != _browserThread) {
_browser = Browser.AttachTo<T>(Find.By("hwnd", _browserHwnd));
_browserThread = currentThreadId;
}
return _browser;
}
set {
_browser = value;
_browserHwnd = _browser.hWnd.ToString();
_browserThread = GetCurrentThreadId();
}
}
private int GetCurrentThreadId() {
return Thread.CurrentThread.GetHashCode();
}
}
ETrade's login pages have changed as well. They have several. All the login pages I checked consistently had a USER field and a PASSWORD field, but the login buttons had various names that look fragile. So if this doesn't work, that's the first thing I'd check.
Second, if I go directly to the auth page, it prompts to log in, but then it frequently doesn't take you to the auth page.
I got more consistent results by going to the home page to log in, then going to the auth page.
static public string GetPin(string username, string password, string logonLink, string authLink) {
// Settings.Instance.MakeNewIeInstanceVisible = false;
var StaticInstanceHelper = new StaticBrowserInstanceHelper<IE>();
Settings.AutoStartDialogWatcher = false;
// This code doesn't always handle it well when IE is already running, but it won't be in my case. You may need to attach to existing, depending on your context.
using (StaticInstanceHelper.Browser = new IE(logonLink)) {
string authCode = "";
// Browser reference was failing because IE hadn't started up yet.
// I'm in the background, so I don't care how long it takes.
// You may want to do a WaitFor to make it snappier.
Thread.Sleep(5000);
if (StaticInstanceHelper.Browser.ContainsText("Scheduled System Maintenance")) {
throw new ApplicationException("eTrade down for maintenance.");
}
TextField user = StaticInstanceHelper.Browser.TextField(Find.ByName("USER"));
TextField pass2 = StaticInstanceHelper.Browser.TextField(Find.ByName("PASSWORD"));
// Class names of the Logon and Logoff buttons vary by page, so I find by text. Seems likely to be more stable.
Button btnLogOn = StaticInstanceHelper.Browser.Button(Find.ByText("Log On"));
Element btnLogOff = StaticInstanceHelper.Browser.Element(Find.ByText("Log Off"));
Button btnAccept = StaticInstanceHelper.Browser.Button(Find.ByValue("Accept"));
TextField authCodeBox = StaticInstanceHelper.Browser.TextField(Find.First());
if (user != null && btnLogOn != null &&
user.Exists && pass2.Exists && btnLogOn.Exists) {
user.Value = username;
pass2.Value = password;
btnLogOn.Click();
}
Thread.Sleep(1000);
if (StaticInstanceHelper.Browser.ContainsText("Scheduled System Maintenance")) {
Element btnContinue = StaticInstanceHelper.Browser.Element(Find.ByName("continueButton"));
if (btnContinue.Exists)
btnContinue.Click();
}
btnLogOff.WaitUntilExists(30);
// Here we go, finally.
StaticInstanceHelper.Browser.GoTo(authLink);
btnAccept.WaitUntilExists(30);
btnAccept.Click();
authCodeBox.WaitUntilExists(30);
authCode = authCodeBox.Value;
StaticInstanceHelper.Browser.Close();
return authCode;
}
}
Being able to automate it like this means that I no longer care about how long the token is valid. Thanks BradM!
This was amazingly helpful. I used your code plus what was posted here to automate this (because tokens expire daily): E*Trade API frequently returns HTTP 401 Unauthorized when fetching an access token but not always
I made two edits:
Changed the authorize URL to what was posted here: https://seansoper.com/blog/connecting_etrade.html
For the log on button, changed it to search by ID: Button btnLogOn = StaticInstanceHelper.Browser.Button(Find.ById("logon_button"));
I ran into issues with Watin and setting up the Apartmentstate. So did this:
static void Main(string[] args)
{
System.Threading.Thread th = new Thread(new ThreadStart(TestAuth));
th.SetApartmentState(ApartmentState.STA);
th.Start();
th.Join();
}
Then put the code in the TestAuth method.

Genesys Platform : Get Call Details From Sip Server

I want to get Call Details from Genesys Platform SIP Server.
And Genesys Platform has Platform SDK for .NET .
Anybod has a SIMPLE sample code which shows how to get call details using Platform SDK for .NET [ C# ] from SIP Server?
Extra Notes:
Call Details : especially i wanted to get AgentId for a given call
and
From Sip Server : I am not sure if Sip Server is the best candiate to
take call details. So open to other suggestions/ alternatives
You can build a class that monitor DN actions. Also you watch specific DN or all DN depending what you had to done. If its all about the call, this is the best way to this.
Firstly, you must define a TServerProtocol, then you must connect via host,port and client info.
var endpoint = new Endpoint(host, port, config);
//Endpoint backupEndpoint = new Endpoint("", 0, config);
protocol = new TServerProtocol(endpoint)
{
ClientName = clientName
};
//Sync. way;
protocol.Open();
//Async way;
protocol.BeginOpen();
I always use async way to do this. I got my reason thou :) You can detect when connection open with event that provided by SDK.
protocol.Opened += new EventHandler(OnProtocolOpened);
protocol.Closed += new EventHandler(OnProtocolClosed);
protocol.Received += new EventHandler(OnMessageReceived);
protocol.Error += new EventHandler(OnProtocolError);
Here there is OnMessageReceived event. This event where the magic happens. You can track all of your call events and DN actions. If you go genesys support site. You'll gonna find a SDK reference manual. On that manual quiet easy to understand there lot of information about references and usage.
So in your case, you want agentid for a call. So you need EventEstablished to do this. You can use this in your recieve event;
var message = ((MessageEventArgs)e).Message;
// your event-handling code goes here
switch (message.Id)
{
case EventEstablished.MessageId:
var eventEstablished = message as EventEstablished;
var AgentID = eventEstablished.AgentID;
break;
}
You can lot of this with this usage. Like dialing, holding on a call inbound or outbound even you can detect internal calls and reporting that genesys platform don't.
I hope this is clear enough.
If you have access to routing strategy and you can edit it. You can add some code to strategy to send the details you need to some web server (for example) or to DB. We do such kind of stuff in our strategy. After successful routing block as a post routing strategy sends values of RTargetPlaceSelected and RTargetAgentSelected.
Try this:
>
Genesyslab.Platform.Contacts.Protocols.ContactServer.Requests.JirayuGetInteractionContent
JirayuGetInteractionContent =
Genesyslab.Platform.Contacts.Protocols.ContactServer.Requests.JirayuGetInteractionContent.Create();
JirayuGetInteractionContent.InteractionId = "004N4aEB63TK000P";
Genesyslab.Platform.Commons.Protocols.IMessage respondingEventY =
contactserverProtocol.Request(JirayuGetInteractionContent);
Genesyslab.Platform.Commons.Collections.KeyValueCollection keyValueCollection =
((Genesyslab.Platform.Contacts.Protocols.ContactServer.Events.EventGetInteractionContent)respondingEventY).InteractionAttributes.AllAttributes;
We are getting AgentID and Place as follows,
Step-1:
Create a Custome Command Class and Add Chain of command In ExtensionSampleModule class as follows,
class LogOnCommand : IElementOfCommand
{
readonly IObjectContainer container;
ILogger log;
ICommandManager commandManager;
public bool Execute(IDictionary<string, object> parameters, IProgressUpdater progress)
{
if (Application.Current.Dispatcher != null && !Application.Current.Dispatcher.CheckAccess())
{
object result = Application.Current.Dispatcher.Invoke(DispatcherPriority.Send, new ExecuteDelegate(Execute), parameters, progress);
return (bool)result;
}
else
{
// Get the parameter
IAgent agent = parameters["EnterpriseAgent"] as IAgent;
IIdentity workMode = parameters["WorkMode"] as IIdentity;
IAgent agentManager = container.Resolve<IAgent>();
Genesyslab.Desktop.Modules.Core.Model.Agents.IPlace place = agentManager.Place;
if (place != null)
{
string Place = place.PlaceName;
}
else
log.Debug("Place object is null");
CfgPerson person = agentManager.ConfPerson;
if (person != null)
{
string AgentID = person.UserName;
log.DebugFormat("Place: {0} ", AgentID);
}
else
log.Debug("AgentID object is null");
}
}
}
// In ExtensionSampleModule
readonly ICommandManager commandManager;
commandManager.InsertCommandToChainOfCommandAfter("MediaVoiceLogOn", "LogOn", new
List<CommandActivator>() { new CommandActivator()
{ CommandType = typeof(LogOnCommand), Name = "OnEventLogOn" } });
enter code here
IInteractionVoice interaction = (IInteractionVoice)e.Value;
switch (interaction.EntrepriseLastInteractionEvent.Id)
{
case EventEstablished.MessageId:
var eventEstablished = interaction.EntrepriseLastInteractionEvent as EventEstablished;
var genesysCallUuid = eventEstablished.CallUuid;
var genesysAgentid = eventEstablished.AgentID;
.
.
.
.
break;
}

my zend session name spacing does not work

I am new to Zend and very keen to learn, so I would really appreciate some help and guidance.
I am trying to create a 'method in a class' that will save the session variables of product pages visited by members to a site i.e
i,e examplesite com/product/?producttype= 6
I want to save the number 6 in a session variable. I also do not want to have a global session for the entire site; I just want it for selected pages. So, I guess I have to have Zend_Session::start() on the selected page; but I am not clear how this should be done.
Should I instantiate it in the page view page. i.e products page or do this in the indexAction() method for the products page. I have attempted to instantiate it below but it did not work.
public function rememberLastProductSearched()
{ //my attempt to start a session start for this particular page.
Zend_Session::start();
}
$session->productSearchCategory = $this->_request->getParam('product-search-category');
return" $session->productSearchCategory ";
}
else
{
//echo " nothing there
return " $session->productSearchCategory";
//";
}
}
With the rememberLastProductSearched() method I was trying to get the method to first check whether the user had searched for a new product or just arrived at the page by default. i.e whether he had used the get() action to search for a new product. If the answer is no, then I wanted the system to check whether their had been a previous saved session variable. so in procedural syntax it would have gone like this:
if(isset($_Get['producttype']))
{
//$dbc database connection
$producttype = mysqli_real_escape_string($dbc,trim($_GET['producttype']));
}
else
if(isset($_SESSION['producttype'])){
$producttype = mysqli_real_escape_string($dbc,trim($_SESSION['producttype']));
}
Can you please help me with the Zend/oop syntax. I am totally confused how it should be?
you're asking about simple work flow in an action, it should begin something like:
//in any controller
public function anyAction()
{
//open seesion, start will be called if needed
$session = new Zend_Session_Namespace('products');
//get value
$productCategory = $this->getRequest()->getParam('producttype');
//save value to namespace
$session->productType = $productCategory;
//...
}
now to move this off to a separate method you have to pass the data to the method...
protected function rememberLastProductSearched($productType)
{
//open seesion, start will be called if needed
$session = new Zend_Session_Namespace('products');
$session->productType = $productType;
}
So now if you want to test for presence of a value...
//in any controller
public function anyAction()
{
//open seesion, call the namespace whenever you need to access it
$session = new Zend_Session_Namespace('products');
if (!isset($session->productType)) {
$productCategory = $this->getRequest()->getParam('producttype');
//save value to session
$this->rememberLastProductSearched($productCategory)
} else {
$productCategory = $session->productType;
}
}
That's the idea.
Be mindful of your work flow as it can sometimes be very simple to inadvertently overwrite your session values.
$session = new Zend_Session_Namespace("productSearch");
if ($this->getRequest()->getParam('producttype')) { //isset GET param ?
$session->productType = $this->getRequest()->getParam('producttype');
$searchedProductType = $session->productType;
} else { //take the session saved value
if ($session->productType) {
$searchedProductType = $session->productType;
}
}
//now use $searchedProductType for your query

Pros & cons bean vs SSJS?

I was trying to build a bean that always retrieves the same document ( a counter document), gets the current value, increment it and save the document with the new value. Finally it should return the value to the calling method and that would get me a new sequential number in my Xpage.
Since the Domino objects cannot be serialized or singleton'ed what's the benefit creating a bean doing this, over creating a SSJS function doing the exact same thing?
My bean must have calls to session, database, view and document, which then will be called every time.
The same within the SSJS-function except for session and database.
Bean:
public double getTransNo() {
try {
Session session = ExtLibUtil.getCurrentSession();
Database db = session.getCurrentDatabase();
View view = db.getView("vCount");
view.refresh();
doc = view.getFirstDocument();
transNo = doc.getItemValueDouble("count");
doc.replaceItemValue("count", ++transNo);
doc.save();
doc.recycle();
view.recycle();
} catch (NotesException e) {
e.printStackTrace();
}
return transNo;
}
SSJS:
function getTransNo() {
var view:NotesView = database.getView("vCount");
var doc:NotesDocument = view.getFirstDocument();
var transNo = doc.getItemValueDouble("count");
doc.replaceItemValue("count", ++transNo);
doc.save();
doc.recycle();
view.recycle();
return transNo;
}
Thank you
Both pieces of code are not good (sorry to be blunt).
If you have one document in your view, you don't need a view refresh which might be queued behind a refresh on another view and be very slow. Presumably you are talking about a single sever solution (since replication of the counter document would for sure lead to conflicts).
What you do in XPages is to create a Java class and declare it as application bean:
public class SequenceGenerator {
// Error handling is missing in this class
private double sequence = 0;
private String docID;
public SequenceGenerator() {
// Here you load from the document
Session session = ExtLibUtil.getCurrentSession();
Database db = session.getCurrentDatabase();
View view = db.getView("vCount");
doc = view.getFirstDocument();
this.sequence = doc.getItemValueDouble("count");
this.docID = doc.getUniversalId();
Utils.shred(doc, view); //Shred currenDatabase isn't a good idea
}
public synchronized double getNextSequence() {
return this.updateSequence();
}
private double updateSequence() {
this.sequence++;
// If speed if of essence I would spin out a new thread here
Session session = ExtLibUtil.getCurrentSession();
Database db = session.getCurrentDatabase();
doc = db.getDocumentByUnid(this.docID);
doc.ReplaceItemValue("count", this.sequence);
doc.save(true,true);
Utils.shred(doc);
// End of the candidate for a thread
return this.sequence;
}
}
The problem for the SSJS code: what happens if 2 users hit that together? At least you need to use synchronized there too. Using a bean makes it accessible in EL too (you need to watch out not to call it too often). Also in Java you can defer the writing back to a different thread - or not write it back at all and in your class initialization code read the view with the actual documents and pick the value from there.
Update: Utils is a class with static methods:
/**
* Get rid of all Notes objects
*
* #param morituri = the one designated to die, read your Caesar!
*/
public static void shred(Base... morituri) {
for (Base obsoleteObject : morituri) {
if (obsoleteObject != null) {
try {
obsoleteObject.recycle();
} catch (NotesException e) {
// We don't care we want go get
// rid of it anyway
} finally {
obsoleteObject = null;
}
}
}
}