Good day, all.
I have a servlet, in which I have local variables declared, initiated and assigned to session attributes.
I need the servlet to be thread-safe, such that multiple browser windows on same computer can have the servlet running and you could perform calculations in each window, but such that the operations in the various threads would not influence each other, that is, that the account balance variable was not shared among the threads, causing inconsistent states of it for any thread at any time.
The below is my code:
// Import servlet and HTTP functionality packages.
import javax.servlet.*;
import javax.servlet.http.*;
// Import packages to: handle user inputs and outputs, enable usage of decimal formatting of numbers.
import java.io.*;
import java.util.*;
import java.text.DecimalFormat;
public class SessionBank extends HttpServlet // Define concrete class and extend HTTP functionality.
{
public void init() throws ServletException // Initialise variables at start of the servlet. Possible exception.
{
}
// The method to output the initial HTML form to the screen, addressing also possible exceptions.
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
// Declare and initiate local variables.
double balance = 0; // The current balance variable, un-formatted number, initiate to zero.
String formattedBal = ""; // The current balance variable, formatted to show as currency amount, initially blank.
// Set balance and formatted balance as session attributes.
request.getSession().setAttribute("balance", balance);
request.getSession().setAttribute("formattedBal", formattedBal);
showBalance(request, response); // Call custom-defined method to display initial page.
}
// Method to respond to user's button inputs - output relevant HTML back to the screen. Possible exceptions.
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
HttpSession session = request.getSession(true); // Establish session object.
response.setContentType("text/html"); // Set response type to text/HTML.
response.setHeader("Expires", "Tues, 01 Jan 1980 00:00:00 GMT"); // Set past date to forbid cache.
// If user clicks "Deposit" button:
if(request.getParameter("depButton") != null && request.getParameter("depButton").equals("Deposit"))
{
if(verifyAmount(request)) // If entered amount passes verification.
{
addToBalance(request, session); // Add the amount to current balance.
redisplayCurrentPage(response); // Redisplay initial page.
}
else // If entered amount does not pass verification.
{
showErrorPage(response); // Display error page.
}
}
// If user clicks "Withdraw" button:
if(request.getParameter("withdrButton") != null && request.getParameter("withdrButton").equals("Withdraw"))
{
if(verifyAmount(request)) // If entered amount passes verification.
{
subtractFromBalance(request, session); // Subtract the amount from current balance.
redisplayCurrentPage(response); // Redisplay initial page.
}
else // If entered amount does not pass verification.
{
showErrorPage(response); // Display error page.
}
}
// If user clicks "Balance" button:
if(request.getParameter("balButton") != null && request.getParameter("balButton").equals("Balance"))
{
showBalance(request, response); // Display current formatted balance on page.
}
}
private boolean verifyAmount(HttpServletRequest request) // Method to verify entered amount, based on textbook criteria.
{
boolean amountValid = false; // Declare and initiate a validity variable.
// If entered amount is not blank and is greater than zero, return validity as true. Else, return false.
if(request.getParameter("amount") != "" && Double.parseDouble(request.getParameter("amount")) > 0)
amountValid = true;
else
amountValid = false;
return amountValid; // Return validity variable.
}
// Method to add amount to balance, addressing possible exception.
private void addToBalance(HttpServletRequest request, HttpSession session) throws IOException
{
double userAmount = Double.parseDouble(request.getParameter("amount")); // Declare and assign entered amount variable.
// Down-cast session attribute object to String, then parse into double type variable.
double balOld = Double.parseDouble(String.valueOf(session.getAttribute("balance")));
double balNew = balOld + userAmount; // Add the amount to current balance and save the value.
session.setAttribute("balance", balNew); // Assign new balance to the session attribute.
}
// Method to subtract from balance. Possible exception.
private void subtractFromBalance(HttpServletRequest request, HttpSession session) throws IOException
{
double userAmount = Double.parseDouble(request.getParameter("amount")); // Declare and assign entered amount value.
// Down-cast session attribute object to String, then parse into a double type variable.
double balOld = Double.parseDouble(String.valueOf(session.getAttribute("balance")));
double balNew = balOld - userAmount; // Subtract the amount from the balance and save the value.
session.setAttribute("balance", balNew); // Assign new balance value to the session attribute.
}
private void showBalance(HttpServletRequest request, HttpServletResponse response) throws IOException // Method to output balance HTML page. Possible exception.
{
PrintWriter out = response.getWriter(); // Establish HTML writer object.
formatBalance(request); // Format current balance for displaying.
out.println("<html>");
out.println("<hr>"); // Horizontal line.
out.println("<title>Online Bank ATM Simulator</title>"); // Title to show on browser title bar.
out.println("<h1 align = \"center\">Bank ATM Simulation</h1>"); // Page heading, centered on page.
out.println("<body onLoad = \"amount.focus()\">"); // Set focus to the text-field.
out.println("<form method = \"POST\" action = \"../servlet/SessionBank\">"); // Form method and submission address.
out.println("<center>"); // Tag to center the following output on page.
out.println("Amount: ");
out.println("<input type = \"text\" name = \"amount\" id = \"amount\" size = \"20\"><br><br>"); // Amount text field.
out.println("Balance: ");
out.println(request.getSession().getAttribute("formattedBal") + "<br><br>"); // Current formatted balance shown.
out.println("<button name = \"balButton\" value = \"Balance\">Balance</button>"); // "Balance" button.
out.println(" "); // Spacers.
out.println("<button name = \"depButton\" value = \"Deposit\">Deposit</button>"); // "Deposit" button.
out.println(" "); // Spacers.
out.println("<button name = \"withdrButton\" value = \"Withdraw\">Withdraw</button>"); // "Withdraw" button.
out.println("</center>"); // Tag to end centering of output on page.
out.println("</form>"); // End of form.
out.println("</body>");
out.println("<br>");
out.println("<hr>"); // Horizontal line.
out.println("</html>");
}
// Method to redisplay form after deposit/withdrawal.
private void redisplayCurrentPage(HttpServletResponse response) throws IOException
{
PrintWriter out = response.getWriter(); // Establish HTML writer object.
out.println("<html>");
out.println("<hr>"); // Horizontal line.
out.println("<title>Online Bank ATM Simulator</title>"); // Title to show on browser title bar.
out.println("<h1 align = \"center\">Bank ATM Simulation</h1>"); // Page heading, centered on page.
out.println("<body onLoad = \"amount.focus()\">"); // Set focus to the text-field.
out.println("<form method = \"POST\" action = \"../servlet/SessionBank\">"); // Form method and submission address.
out.println("<center>"); // Tag to center the following output on page.
out.println("Amount: ");
out.println("<input type = \"text\" name = \"amount\" id = \"amount\" size = \"20\"><br><br>"); // Amount text field.
out.println("Balance: ");
out.println("<br><br>"); // No formatted balance value shown.
out.println("<button name = \"balButton\" value = \"Balance\">Balance</button>"); // "Balance" button.
out.println(" "); // Spacers.
out.println("<button name = \"depButton\" value = \"Deposit\">Deposit</button>"); // "Deposit" button.
out.println(" "); // Spacers.
out.println("<button name = \"withdrButton\" value = \"Withdraw\">Withdraw</button>"); // "Withdraw" button.
out.println("</center>"); // Tag to end centering of output on page.
out.println("</form>"); // End of form.
out.println("</body>");
out.println("<br>");
out.println("<hr>"); // Horizontal line.
out.println("</html>");
}
private void formatBalance(HttpServletRequest request) // Method to format the current balance number to a currency amount.
{
DecimalFormat dollars = new DecimalFormat("$###,###.###"); // Construct new decimal format.
// Down-cast session attribute to String, parse to double, then format using the above decimal format and save value.
String formattedBal = dollars.format(Double.parseDouble(String.valueOf(request.getSession().getAttribute("balance"))));
request.getSession().setAttribute("formattedBal", formattedBal); // Assign new formatted balance to session attribute.
}
// Method to output error HTML page, if entered amount does not pass verification. Possible exception.
private void showErrorPage(HttpServletResponse response) throws IOException
{
PrintWriter out = response.getWriter(); // Establish HTML writer object.
out.println("<html>");
out.println("<head>");
out.println("<title>Amount Input Error</title>"); // Title to show in browser title bar.
out.println("</head>");
out.println("<body>");
out.println("<h1>Error processing the input.</h1><br>"); // Heading text.
out.println("Please ensure your input:<br><br>");
out.println("- Is not blank.<br>");
out.println("- Is strictly a number.<br>");
out.println("- Is a positive, non-zero amount.");
out.println("</body>");
out.println("</html>");
}
public void destroy() // Method to terminate the servlet.
{
}
}
Now, what happens instead is, when using multiple threads (browser windows running the servlet), the balance that one thread overwrites, becomes the balance that another thread reads. Which is of course incorrect. I need each simultaneous thread to have its own copy of this variable for consistent results.
As far as I know, unlike storing the balance as a class (instance) variable, storing it as local and assigning to session attribute is thread-safe. But if that is so, why do the threads update one another's variable? What is coded incorrectly?
Thanks a lot!
Browsers can be a bit inconsistent with this but ultimately what you're seeing is that a single browser is interacting with your server with a single session cookie (usually named JSESSIONID). From the perspective of the server and your code it doesn't know that there are multiple tabs or browsers because of the single cookie.
In this case one thread accesses the value from the session, updates and stores it. Another thread - servicing a different browser tab - does the same thing. But there is only one session and one variable in the session so it is ultimately shared between the browser tabs.
Solving this can be a bit of a challenge in a cross platform way. One way is to have some JavaScript in the browser generate a unique number or code when it starts up. So, as an example, you create an onload method in your HTML code and use a simple Math.random() JavaScript call. Then, for every request, you pass this number to the back end. On the back end side you create a Map that maps the JavaScript generated number to another Map of parameters and store that in the session.
Your code makes it a bit more difficult to implement as you've got both the display and backend logic in one place (usually it would be a JSP for the display and the servlet for the backend) but it could be done.
Related
I have problems with my dialog field. I have button that opens dialog tab with field. It was supposed to show on that field lookup exact records(i guess i need select there by one field value). Right now i have this code:
DialogField Journal = dialog.addField(extendedTypeStr(JournalId));
This dialog line adds a field with all values on that EDT. I have 3 journal types - NEW, UPDATE, DELETE. Right now on that field lookup it shows me all 3 journal types. I want to make custom lookup that shows exact type , example - if i click that button on journal that has type "NEW", then it should show only "NEW" type of journal types on lookup. I heard there is something like dialog.addLookup or something. Can someone help me?
You already added your dialog field (in the dialog() method). Now add the dialogRunPost() method that is executed after the form GUI is initialized. At that point you can fetch the underlying FormStringControl behind the dialog field. Subscribing to the FormStringControl.OnLookup event allows you to override the lookup.
I did not have some journal data available, so I created a similar example with customers. My example dialog (MyDialog) takes a source customer (customerCaller) and shows a dialog with a custom lookup that only shows customers with the same customer group.
My example is also a standalone, runnable class and is not called from a form. Comments have been added to indicate where this affects the code.
Full example
public class MyDialog extends Runbase
{
// fields
protected Args args;
protected CustTable customerCaller;
protected DialogField dfCustomerId;
// construct
public static MyDialog newArgs(Args _args)
{
MyDialog ret = new MyDialog();
ret.args = _args;
return ret;
}
// initialize
public boolean init()
{
boolean ret = super();
// validate and fetch caller
if (args.record() && args.record().TableId == tableNum(CustTable))
//if (args.caller() && args.caller().dataset() == tableNum(CustTable)) --> when called from form
{
customerCaller = args.record();
//customerCaller = args.caller().record();
}
else
{
throw error(Error::missingRecord('My Dialog'));
}
return ret;
}
// build dialog
public Object dialog()
{
Dialog ret = super();
// optional reference to visualize the input
ret.addText('Caller customer group = ' + customerCaller.CustGroup);
// add field
dfCustomerId = ret.addField(extendedTypeStr(CustAccount)); // default lookup = all CustTable.AccountNum values
return ret;
}
public void dialogPostRun(DialogRunbase dialog)
{
super(dialog);
// subscribe to lookup event
FormStringControl fscCustomerId = dfCustomerId.control();
fscCustomerId .OnLookup += eventhandler(this.customerId_OnLookup);
}
// custom lookup for customer id
protected void customerId_OnLookup(FormControl _sender, FormControlEventArgs _e)
{
// cancel default
FormControlCancelableSuperEventArgs eventArgs = _e;
eventArgs.CancelSuperCall();
// define lookup query (list all customers with same customer group as input customer)
Query query = new Query();
QueryBuildDataSource qbds = SysQuery::findOrCreateDataSource(query, tableNum(CustTable));
SysQuery::findOrCreateRange(qbds, fieldNum(CustTable, CustGroup)).value(SysQuery::value(customerCaller.CustGroup));
// do lookup
SysTableLookup lookup = SysTableLookup::newParameters(tableNum(CustTable), _sender);
lookup.parmQuery(query);
lookup.addLookupfield(fieldNum(CustTable, AccountNum), true);
lookup.addLookupfield(fieldNum(CustTable, CustGroup));
lookup.performFormLookup();
}
// run dialog
public static void main(Args _args)
{
// I am running this dialog directly (not from a form), generating some random input
CustTable customer;
select firstonly customer where customer.CustGroup != '';
_args.record(customer);
// end of random input
MyDialog md = MyDialog::newArgs(_args);
md.init();
if (md.prompt())
{
md.run();
}
}
}
Result
I want to learn how to manage the state of a page between navigation.
for example a navigate onto page1 and then i navigate to page2, but when i navigate back to page1, the UI elements must already be there with the same data as before and they must not be re-initialized or data must not be binded again by the compiler.
Also what I can do to manage state of whole application such that, I terminate the app and then when i launch it next time, the same state is already there as last time. can i apply it on whole application? or what if I only want to apply it on a few pages? any help would be appreciated thanks.
or example a navigate onto page1 and then i navigate to page2, but when i navigate back to page1, the UI elements must already be there with the same data as before and they must not be re-initialized or data must not be binded again by the compiler.
For this question, you may use UIElement.CacheMode property and Frame.CacheSize property. CacheSize property sets the number of pages in the navigation history that can be cached for the frame, and CacheMode property sets a value that indicates that rendered content should be cached as a composited bitmap when possible.
As we know, an UWP app default using a rootFrame for displaying several pages, we just use Navigation method to change the content in the frame. You can see this in the OnLaunched(LaunchActivatedEventArgs e) method of a blank UWP app. But how to implement cache function? For example, your app has two page and one root frame. You can define CacheSize property in your OnLaunched(LaunchActivatedEventArgs e) method for example:
protected override void OnLaunched(LaunchActivatedEventArgs e)
{
...
// Ensure the current window is active
rootFrame.CacheSize = 2;
Window.Current.Activate();
}
Then in your two pages's constructed functions enable CacheMode property for example:
public MainPage()
{
this.InitializeComponent();
this.NavigationCacheMode = NavigationCacheMode.Enabled;
}
Also what I can do to manage state of whole application such that, I terminate the app and then when i launch it next time, the same state is already there as last time. can i apply it on whole application?
For this question, you will need to save the page state in the OnSuspending(object sender, SuspendingEventArgs e) method using Frame.GetNavigationState method, and you can save this state into the app's local settings. For example:
private void OnSuspending(object sender, SuspendingEventArgs e)
{
var deferral = e.SuspendingOperation.GetDeferral();
Frame rootFrame = Window.Current.Content as Frame;
string navstate = rootFrame.GetNavigationState();
var localSettings = ApplicationData.Current.LocalSettings;
localSettings.Values["nav"] = navstate;
deferral.Complete();
}
And how to retrieve this informaton? You can override your OnLaunched(LaunchActivatedEventArgs e) method, and at first you will need to judge how is your app be closed last time, by user, or by system using ApplicationExecutionState enumeration, for example like this:
protected override void OnLaunched(LaunchActivatedEventArgs e)
{
//#if DEBUG
// if (System.Diagnostics.Debugger.IsAttached)
// {
// this.DebugSettings.EnableFrameRateCounter = true;
// }
//#endif
Frame rootFrame = Window.Current.Content as Frame;
// Do not repeat app initialization when the Window already has content,
// just ensure that the window is active
if (rootFrame == null)
{
// Create a Frame to act as the navigation context and navigate to the first page
rootFrame = new Frame();
rootFrame.NavigationFailed += OnNavigationFailed;
if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
{
//TODO: Load state from previously suspended application
}
// Place the frame in the current Window
Window.Current.Content = rootFrame;
}
if (rootFrame.Content == null)
{
// When the navigation stack isn't restored navigate to the first page,
// configuring the new page by passing required information as a navigation
// parameter
//rootFrame.Navigate(typeof(MainPage), e.Arguments);
if (e.PreviousExecutionState == ApplicationExecutionState.Terminated ||
e.PreviousExecutionState == ApplicationExecutionState.ClosedByUser)
{
object value;
var localSettings = ApplicationData.Current.LocalSettings;
if (localSettings.Values.TryGetValue("nav", out value))
{
rootFrame.SetNavigationState(value as string);
}
else
{
rootFrame.Navigate(typeof(MainPage), e.Arguments);
}
}
else
{
rootFrame.Navigate(typeof(MainPage), e.Arguments);
}
}
// Ensure the current window is active
rootFrame.CacheSize = 2;
Window.Current.Activate();
}
But be aware that when an app is closed, next time you launch this app, the UI elements will be re-initialized, this function can only navigate to the page when the last time you close your app, but the data in that page will be lost. But you can also save the data to the local settings and when you navigate to the page, set the value to those UI elements.
I've been dabbling a little into MVC 5's custom client validation (MVC's GetClientValidationRules and jQuery's validator). I've been able to successfully implement a validator to check whether the user selects a date that is in the past. But what about if I need to check if a user's textbox input is greater than another textbox's input of theirs?
I have it working fine without unobtrusive validation/server-side, but not with unobtrusive validation.
Here's an example.
Model
public Nullable<int> ItemsPurchased
public Nullable<int> ItemsReturned
A custom DataAnnotation has been made for ItemsReturned to check whether its value <= ItemsPurchased. Implementing GetClientValidationRules to actually get ItemsPurchased's current value is where I'm having trouble in the code.
I had a variable in my custom data annotation. I'm not sure why this didn't come to me naturally...but here's my solution.
DataAnnotation (Applied to ItemsReturned)
public string purchasedQuantityField { get; set; }
public ReturnedAttribute(string quantity) {
purchasedQuantityField = quantity;
}
...
(in GetClientValidationRules)
ModelClientvalidationRule purchaseRule = new ModelClientValidationRule();
purchaseRule.ValidationType = "purchaserestriction";
purchaseRule.ErrorMessage = ErrorMessageString;
purchaseRule.ValidationParameters["otherfield"] = purchasedQuantityField;
Usage in the model:
[Returned("ItemsPurchased", ErrorMessage = "Returned Items cannot be greater than the number of Items Purchased.")]
Then I made my own JS file with the custom client validation rules, CustomClientValidation.js:
jQuery.validator.addMethod("purchaserestriction", function (value, element, params) {
var purchasedFieldVal = $('input[name="' + params.otherfield + '"]').val();
if (purchasedFieldVal && value) {
var returnedVal = parseInt(value);
var purchasedVal = parseInt(purchasedFieldVal);
if (returnedVal <= purchasedVal) return true;
else return false;
}
);
jQuery.validator.unobtrusive.adapters.add("purchaserestriction", ["otherfield"], function (options) {
options.rules["purchaserestriction"] = options.params;
if (options.message) options.messages["purchaserestriction"] = options.message;
});
It's possible with jQuery Validation. You can create custom rules which will check whatever you want. Take a look at this:
$.validator.addMethod('name_of_the_rule', function (value, element, param) {
//put there your validation
return isValid; // return bool here if valid or not.
}, 'Put here your error message!');
$('#form').validate({
rules: {
field1: {
yourRuleName: true
}
}
});
How do I save values between page navigation in windows phone,
suppose I have two text blocks in my one phone application page, and they contains dynamically changing values every time, now suppose my text block have value "abc" and for some reason I go back to previous page, now when I get back on my page, I want that text block having value "abc". How to do it??
There are several methods available
IsolatedStorageSettings
Save
IsolatedStorageSettings settings = IsolatedStorageSettings.ApplicationSettings;
// txtInput is a TextBox defined in XAML.
if (!settings.Contains("userData"))
{
settings.Add("userData", txtInput.Text);
}
else
{
settings["userData"] = txtInput.Text;
}
settings.Save();
Read
if (IsolatedStorageSettings.ApplicationSettings.Contains("userData"))
{
txtDisplay.Text +=
IsolatedStorageSettings.ApplicationSettings["userData"] as string;
}
PhoneApplicationService.Current.State
PhoneApplicationService.Current.State["param"] = param
and on other page we can get it like this.
var k = PhoneApplicationService.Current.State["param"];
Define two static variable in your App.xaml.cs
public static valueOne = string.Empty;
public static valueTwo = string.empty;
//Assign textbox value to variable on page leaving event
protected override void OnNavigatingFrom(System.Windows.Navigation.NavigatingCancelEventArgs e)
{
if(!string.IsNullOrEmpty(txtBoxOne.Text))
App.valueOne = txtBoxOne.Text;
if(!string.IsNullOrEmpty(txtBoxTwo.Text))
App.valueTwo = txtBoxTwo.text;
}
//Get value from page load
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if(!string.IsNullOrEmpty(App.valueOne))
string valueFirst = App.valueOne;
if(!string.IsNullOrEmpty(App.valueTwo ))
string valueTwo = App.valueTwo ;
}
There are various approaches to solve this.
Common thing is using a Static Class, which holds static properties and binding it to your View.
This is a part of a larger program that I'm doing where the user can create a Flight object by entering data into JTextFields. The data is stored into a Vector, called flightList. On a second panel of my JApplet, the user can use a JComboBox - flightBox - to select one of the Flights that they have created. When the Flight is selected from the JComboBox, the getPrice() method needs to be called for the selected Flight object, and displayed in a JLabel below.
private class ChoiceListener implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
//needs to be completed
if (flightBox.getSelectedIndex() == 0)
{
//flightBox.getSelectedItem(); // returns selected object
outro.setText("The price for your flight is:");
int p = flightBox.getSelectedIndex();
Flight selectedFlight = flightList.get(p);
String selectedPrice = money.format(selectedFlight.getPrice()) + "";
fPrice.setText(selectedPrice);
}
}
I thought I was on the right track, and I've tried a lot of different variations but none seem to be working.
Also, I know that the Flights are being added to flightList, because the JComboBox does display all added Flights. I've got all the labels set up correctly, I think. I just need to figure out how to actually get the selected Flight object from flightList using the flightBox, and pull that price value from it using the getPrice method.
EDIT
From the CreatePanel class (initializing variables and storing the Flight object into the flightList Vector from JTextFields).
CityTime departure = new CityTime();
departure.setCity(dC);
departure.setDate(dD);
departure.setTime(dT);
CityTime arrival = new CityTime();
arrival.setCity(aC);
arrival.setDate(aD);
arrival.setTime(aT);
Flight newFlight = new Flight();
newFlight.setAirlines(air);
newFlight.setFlightNum(iNum = Integer.parseInt(num));
newFlight.setPrice(dPrc = Double.parseDouble(prc));
newFlight.setDeparture(dC, dD, dT);
newFlight.setArrival(aC, aD, aT);
flightList.add(newFlight);
From the Flight class:
public class Flight
{
// Flight constructor and all other variables/accessors/mutators are added here as well.
private double price;
public double getPrice()
{
return price;
}
}
EDIT
Completed code:
if (flightBox.getSelectedIndex() != -1)
{
//flightBox.getSelectedItem(); // returns selected object
outro.setText("The price for your flight is:");
int p = flightBox.getSelectedIndex();
Flight selectedFlight = flightList.get(p);
String selectedPrice = money.format(selectedFlight.getPrice()) + "";
fPrice.setText(selectedPrice);
}
All flightList Vectors have been updated with the element.