Ok so I have my code done, or so I thought, but it is not loading the account numbers from the csv file that I have to read from. I am completely lost and not understanding why it is not reading those accounts into the combobox. Can someone please help me figure out why the accounts are not showing up in the combobox. The following is Controller Package that runs the program:
import edu.tridenttech.cpt237.bank.model.Bank;
import edu.tridenttech.cpt237.bank.view.StartWindow;
import javafx.application.Application;
import javafx.stage.Stage;
public class MainApp extends Application
{
#Override
public void start(Stage primaryStage) throws Exception
{
Bank bank = new Bank();
bank.loadTransactions("Transactions.csv");
StartWindow ui = new StartWindow(primaryStage);
ui.show();
}
public static void main(String [] args)
{
Application.launch(args);
}
}
The following is the Bank class in the Model Package:
import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Scanner;
public class Bank
{
private static Bank instance = new Bank();
private ArrayList<Account> accounts = new ArrayList<>();
/**
* Gets the Singleton Bank instance
* #return Returns the singleton Bank instance
*/
public static Bank getInstance()
{
return instance;
}
/**
* Open a new savings account and place it in the list of bank accounts.
*
* #param accntNum the number of the new account
* #param initialBal the initial balance
* #return Returns <i>true</i> if an account is created; <i>false</i> if the account already exists or the balance is invalid
*/
public boolean openSavingsAccount(String accntNum, double initialBal)
{
if (findAccountByNum(accntNum) != null || initialBal < 0) {
return false;
}
SavingsAccount savings = new SavingsAccount(accntNum, initialBal);
return accounts.add(savings);
}
/**
* Open a new checking account and place it in the list of bank accounts.
*
* #param accntNum the number of the new account
* #param initialBal the initial balance
* #return Returns <i>true</i> if an account is created; <i>false</i> if the account already exists or the balance is invalid
*/
public boolean openCheckingAccount(String accntNum, double initialBal, double minBalance)
{
if (findAccountByNum(accntNum) != null || initialBal < 0) {
return false;
}
CheckingAccount checking = new CheckingAccount(accntNum, initialBal);
return accounts.add(checking);
}
/**
* Finds the account specified by the given account number
* #param accntNum the number of the account to be found
* #return Returns the account matching the number if found; <i>null</i> if the account is not found
*/
public Account findAccountByNum(String accntNum)
{
Account acnt = null;
Optional<Account> match = accounts.stream().filter(e -> e.getAccountNumber().equals(accntNum)).findFirst();
if (match.isPresent()) {
acnt = match.get();
}
return acnt;
}
/**
* Transfers the specified amount from the fromAccount to the toAccount. This method can fail if either
* of the account numbers is invalid, or if the fromAccount has insufficient funds to make the transfer.
* #param fromAccountNum The account number of the account from which the money is to be withdrawn.
* #param toAccountNum The account number of the account to which the money is to be deposited.
* #param amount The amount to be transfered.
* #return Returns <i>true</i> if the transfer was successful, <i>false</i> otherwise
*/
public boolean makeTransfer(String fromAccountNum, String toAccountNum, double amount)
{
Account fromAccnt;
Account toAccnt;
fromAccnt = findAccountByNum(fromAccountNum);
toAccnt = findAccountByNum(toAccountNum);
if (fromAccnt == null || toAccnt == null) {
return false;
}
if (fromAccnt.withdraw(amount)) {
toAccnt.deposit(amount);
return true;
} else {
return false;
}
}
/**
* Pulls all of the account numbers from the accounts and returns them as a list of strings.
* #return The list of account numbers.
*/
public List<String> getAllAccountNumbers()
{
ArrayList<String> accountNums = new ArrayList<>();
accounts.stream().forEach(e -> accountNums.add(e.getAccountNumber()));
return accountNums;
}
/**
* Loads the transactions from the specified comma separated values file. The format of the file is as follows:
* O,num,type,amount
* D,num,type,amount
* W,num,type,amount
* T,from,to,amount
* #param filePath Path to the file containing the transactions
* #throws FileNotFoundException
*/
public void loadTransactions(String filePath) throws FileNotFoundException
{
Scanner input;
input = new Scanner(new File(filePath));
while (input.hasNext())
{
String line = input.nextLine();
// creates an string array called fields and populates each item
// splitting by comma.
String[] fields = line.split(",");
// System.out.println("number of fields: " + fields.length);
// first field and first character
switch (fields[0].charAt(0)) {
case 'O':
case 'o': {
double minBalance = 0;
// open a new account
String accntNum = fields[1];
String type = fields[2];
double initialBalance = Double.parseDouble(fields[3]);
if (fields.length == 5)
{
minBalance = Double.parseDouble(fields[4]);
}
createAccount(accntNum, type, initialBalance, minBalance);
} break;
case 'D':
case 'd': {
// deposit into an account
String accntNum = fields[1];
String type = fields[2];
double amount = Double.parseDouble(fields[3]);
Account account = findAccountByNum(accntNum);
account.deposit(amount);
} break;
case 'W':
case 'w': {
String accntNum = fields[1];
String type = fields[2];
double amount = Double.parseDouble(fields[3]);
Account account = findAccountByNum(accntNum);
account.withdraw(amount);
} break;
case 'T':
case 't': {
String fromAccount = fields[1];
String toAccount = fields[2];
double amount = Double.parseDouble(fields[3]);
makeTransfer(fromAccount, toAccount, amount);
} break;
default: {
System.out.println("Does not meet requirements");
}
}
}
input.close();
}
private void createAccount(String accntNum, String type, double initialBalance, double minBalance)
{
switch (type.charAt(0)) {
case 's':
case 'S': {
openSavingsAccount(accntNum, initialBalance);
} break;
case 'c':
case 'C': {
openCheckingAccount(accntNum, initialBalance, minBalance);
} break;
}
}
}
And this last class is the StartWindow in the View Package:
import edu.tridenttech.cpt237.bank.model.Bank;
import edu.tridenttech.cpt237.bank.view.NewAccount;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.layout.FlowPane;
import javafx.stage.Stage;
public class StartWindow implements EventHandler<ActionEvent>
{
private Stage myStage;
private Transaction transaction;
private NewAccount account;
private Transfer t;
private ComboBox<String> AccountsCB = new ComboBox<>();
Button display;
Button open;
Button transfer;
Button exit;
public StartWindow(Stage stage)
{
transaction = new Transaction();
FlowPane pane = new FlowPane();
Scene scene = new Scene(pane);
myStage = stage;
myStage.setScene(scene);
myStage.setTitle("Starting Window");
pane.getChildren().add(AccountsCB);
AccountsCB.getItems().setAll(Bank.getInstance().getAllAccountNumbers());
account = new NewAccount(AccountsCB);
display = new Button("Display Current Account");
pane.getChildren().add(display);
display.setOnAction(this);
open = new Button("Open New Account");
pane.getChildren().add(open);
open.setOnAction(this);
transfer = new Button("Transfer Funds Between Accounts");
pane.getChildren().add(transfer);
transfer.setOnAction(this);
t = new Transfer();
exit = new Button("Exit");
pane.getChildren().add(exit);
exit.setOnAction(this);
}
public void handle(ActionEvent event)
{
Button button = (Button) (event.getSource());
if (button == display)
{
if (!transaction.isShowing())
{
transaction.show(AccountsCB.getValue());
}
else
{
transaction.toFront();
}
}
if (button == open)
{
if (!account.isShowing())
{
account.show();
}
else
{
account.toFront();
}
}
if (button == transfer)
{
if (!t.isShowing())
{
t.show();
}
else
{
t.toFront();
}
}
if (button == exit)
{
myStage.close();
}
}
public void show()
{
myStage.show();
}
}
When I run the program, it lets me create a new account, but when I want to transfer funds between accounts or open an existing account, it is not loading the accounts from the Transactions.csv file.
You are attempting to implement Bank as a singleton, but you have not prevented creation of additional instances of Bank. Consequently, in your start() method you are able to create a new instance of Bank and load the transactions in that instance:
Bank bank = new Bank();
bank.loadTransactions("Transactions.csv");
Clearly this is not the same instance of Bank as is returned by Bank.getInstance(), so in your StartWindow class, when you call Bank.getInstance(), you receive a Bank instance with no transactions loaded.
If you want to make Bank a singleton, you should make the constructor private so as to prevent creation of any additional instances:
public class Bank {
private static Bank instance = new Bank();
private ArrayList<Account> accounts = new ArrayList<>();
/**
* Gets the Singleton Bank instance
* #return Returns the singleton Bank instance
*/
public static Bank getInstance() {
return instance;
}
// private constructor:
private Bank() { }
// existing code....
}
and then of course do
#Override
public void start(Stage primaryStage) throws Exception {
Bank.getInstance().loadTransactions("Transactions.csv");
StartWindow ui = new StartWindow(primaryStage);
ui.show();
}
The singleton pattern is generally considered an anti-pattern by many programmers. I would recommend just creating a Bank instance in the usual way and passing it to the StartWindow instance (and to anywhere else that needs it):
public class Bank {
// private static Bank instance = new Bank();
private ArrayList<Account> accounts = new ArrayList<>();
/**
* Gets the Singleton Bank instance
* #return Returns the singleton Bank instance
*/
// public static Bank getInstance() {
// return instance;
//}
/**
* Open a new savings account and place it in the list of bank accounts.
*
* #param accntNum the number of the new account
* #param initialBal the initial balance
* #return Returns <i>true</i> if an account is created; <i>false</i> if the account already exists or the balance is invalid
*/
public boolean openSavingsAccount(String accntNum, double initialBal)
{
if (findAccountByNum(accntNum) != null || initialBal < 0) {
return false;
}
SavingsAccount savings = new SavingsAccount(accntNum, initialBal);
return accounts.add(savings);
}
// existing code ...
}
Then create a bank instance, load its transactions, and pass it to your StartWindow:
public class MainApp extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
Bank bank = new Bank();
bank.loadTransactions("Transactions.csv");
StartWindow ui = new StartWindow(primaryStage, bank);
ui.show();
}
public static void main(String [] args) {
Application.launch(args);
}
}
and
public class StartWindow implements EventHandler<ActionEvent> {
private Bank bank ;
private Stage myStage;
private Transaction transaction;
private NewAccount account;
private Transfer t;
private ComboBox<String> AccountsCB = new ComboBox<>();
Button display;
Button open;
Button transfer;
Button exit;
public StartWindow(Stage stage, Bank bank) {
this.bank = bank ;
transaction = new Transaction();
FlowPane pane = new FlowPane();
Scene scene = new Scene(pane);
myStage = stage;
myStage.setScene(scene);
myStage.setTitle("Starting Window");
pane.getChildren().add(AccountsCB);
// note:
AccountsCB.getItems().setAll(bank.getAllAccountNumbers());
account = new NewAccount(AccountsCB);
display = new Button("Display Current Account");
pane.getChildren().add(display);
display.setOnAction(this);
open = new Button("Open New Account");
pane.getChildren().add(open);
open.setOnAction(this);
transfer = new Button("Transfer Funds Between Accounts");
pane.getChildren().add(transfer);
transfer.setOnAction(this);
t = new Transfer();
exit = new Button("Exit");
pane.getChildren().add(exit);
exit.setOnAction(this);
}
// etc ...
}
Related
I am using a JFXComboBox to show user group a user can be allocated to. The roles are fetched from the DB. Fetching of the rolelist is done by getRoleNameList(). I populate the JFXComboBox as below:
Populate userGroup JFXComboBox
// Populate userGroup JFXComboBox
ObservableList<Role> roles = rdc.getRoleNameList();
roles.sort(Comparator.comparing(Role::getCode)); // Sort the list
uGroupComboBox.getItems().setAll(roles);
uGroupComboBox.setVisibleRowCount(5);
JFXCombobox FXML:
<JFXComboBox id="userGroup" fx:id="uGroupComboBox" focusColor="#07595a"
layoutX="245.0" layoutY="256.0" prefHeight="30.0" prefWidth="148.0"
promptText="Select User Group" styleClass="jfx-combo-box"
unFocusColor="#48aaad">
The model below is for Roles:
package records.models;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import org.apache.commons.lang3.StringUtils;
public class Role {
private final StringProperty code;
private final StringProperty name;
public Role() {
this.name = new SimpleStringProperty();
this.code = new SimpleStringProperty();
}
public Role(String name) {
String codename = StringUtils.capitalize(name.toLowerCase().trim());
this.code = new SimpleStringProperty(codename);
name = name.toLowerCase().trim();
this.name = new SimpleStringProperty(name);
}
// name
public String getName() {
return name.get();
}
public void setName(String name) {
if (name != null) {
name = name.toLowerCase().trim();
}
this.name.set(name);
}
public StringProperty nameProperty() {
return name;
}
// #return code
public String getCode() {
return code.get();
}
public void setCode(String code) {
if (code != null) {
code = StringUtils.capitalize(code.toLowerCase().trim());
}
this.code.set(code);
}
public StringProperty codeProperty() {
return code;
}
public Role getRole(){
return this;
}
#Override
public String toString(){
return this.code.get();
}
}
When I fetch from the DB the user group the user is assigned to, the data is not displayed:
String uRole = role.get("role") == null ? null :
role.get("role").toString();
user.setRole(uRole);
// Create the role that is fetched from DB
Role uGroup = new Role(uRole);
// uGroupComboBox.setValue(uGroup);
uGroupComboBox.getSelectionModel().select(uGroup);
This does not work. As you can see, the field is not null. If it were, I would show the prompt text
However, the JFXComboBox list is successfully populated:
If I do System.out.println(uGroupComboBox.getValue()); it shows the correct value eg. if the user group is Nurse, the statement returns Nurse
Why does the JFXComboBox not display the value?
The problem is in your Role object with its StringProperty properties. Let me explain myself.
if you just take :
static void main(String[] args){
StringProperty codeName = new SimpleStringProperty("codename");
StringProperty codeNameSameValue = new SimpleStringProperty("codename");
boolean isthesame = codeName.equals(codeNameSameValue);
System.out.println("isthesame:"+isthesame);
boolean isReallyTheSame = codeName.get().equals(codeNameSameValue.get());
System.out.println("isReallyTheSame:"+isReallyTheSame);
}
You will see on you console output
isthesame:false
isReallyTheSame:true
Ok it is still not the answer to your question but for you Role class default equals method that will be comparing object properties like my first comparison:
comparingStringProperty.equals(comparedStringProperty);
...
To solve the problem you need to override the equals method in the Role object. For example with something like that:
i
mport java.util.Objects;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import org.apache.commons.lang3.StringUtils;
public class Role {
private final StringProperty code;
private final StringProperty name;
/*Constructors, setters and getter are deleted for clarity */
....
#Override
public String toString() {
return this.code.get();
}
#Override
public boolean equals(Object o) {
// If the object is compared with itself then return true
if (o == this) {
return true;
}
/* Check if o is an instance of Role or not
"null instanceof [type]" also returns false */
if (!(o instanceof Role)) {
return false;
}
// typecast o to Role so that we can compare data members
Role r = (Role) o;
return (r.name.get() == this.name.get() || r.name.get() != null && r.name.get().equals(this.name.get()));
}
#Override
public int hashCode() {
return Objects.hash(name.get(), code.get());
}
}
This equals method will be use during the call :
uGroupComboBox.getSelectionModel().select(uGroup);
And voila!
I have a case where I need to filter a ObservableList<Item> based on some properties of the items (i.e. the condition is internal and not external). I found out that javafx has FilteredList so I tried it. I could set the predicate and filtering works, until the property value that determines the filtering changes. Setting the predicate is done now like following:
list.setPredicate(t -> !t.filteredProperty().get())
Since the predicate returns boolean and not BooleanProperty, the changes to that property are not reflected on the list.
Is there any easy solution to this? I could try to do some workarounds, e.g. create a separate list and sync that, or reset the predicate every time the property changes in one item hopefully retriggering the filtering, but I first wanted to ask in case someone knows a pretty solution as those workarounds certainly are not.
Create the underlying list with an extractor. This will enable the underlying list to fire update events when the filteredProperty() of any elements change. The FilteredList will observe these events and so will update accordingly:
ObservableList<Item> baseList = FXCollections.observableArrayList(item ->
new Observable[] {item.filteredProperty()});
FilteredList<Item> list = new FilteredList<>(baseList, t -> ! t.filteredProperty().get());
Quick demo:
import java.util.stream.IntStream;
import javafx.beans.Observable;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener.Change;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
public class DynamicFilteredListTest {
public static void main(String[] args) {
ObservableList<Item> baseList = FXCollections.observableArrayList(item ->
new Observable[] {item.filteredProperty()});
FilteredList<Item> list = new FilteredList<>(baseList, t -> ! t.isFiltered());
list.addListener((Change<? extends Item> c) -> {
while (c.next()) {
if (c.wasAdded()) {
System.out.println(c.getAddedSubList()+ " added to filtered list");
}
if (c.wasRemoved()) {
System.out.println(c.getRemoved()+ " removed from filtered list");
}
}
});
System.out.println("Adding ten items to base list:\n");
IntStream.rangeClosed(1, 10).mapToObj(i -> new Item("Item "+i)).forEach(baseList::add);
System.out.println("\nFiltered list now:\n"+list);
System.out.println("\nSetting filtered flag for alternate items in base list:\n");
IntStream.range(0, 5).map(i -> 2*i).mapToObj(baseList::get).forEach(i -> i.setFiltered(true));
System.out.println("\nFiltered list now:\n"+list);
}
public static class Item {
private final StringProperty name = new SimpleStringProperty() ;
private final BooleanProperty filtered = new SimpleBooleanProperty() ;
public Item(String name) {
setName(name);
}
public final StringProperty nameProperty() {
return this.name;
}
public final String getName() {
return this.nameProperty().get();
}
public final void setName(final String name) {
this.nameProperty().set(name);
}
public final BooleanProperty filteredProperty() {
return this.filtered;
}
public final boolean isFiltered() {
return this.filteredProperty().get();
}
public final void setFiltered(final boolean filtered) {
this.filteredProperty().set(filtered);
}
#Override
public String toString() {
return getName();
}
}
}
If you are using database loading function plus need to filter multiple fields, this solution will help.
ObservableList<PurchaseOrder> poData = FXCollections.observableArrayList();
FilteredList<PurchaseOrder> filteredData;
private void load() {
PurchaseOrder po = new PurchaseOrder();
try {
poData = po.loadTable("purchase_orders", beanFields); // Database loading data
} catch (SQLException ex) {
Logger.getLogger(PurchaseOrdersController.class.getName()).log(Level.SEVERE, null, ex);
}
filteredData = new FilteredList<>(poData, t -> true); //Encapsulate data with filter
poTable.setItems(filteredData); //Load filtered data into table
//Set event trigger for all filter textboxes
txtFilter.textProperty().addListener(obs->{
filter(filteredData);
});
txtFilter2.textProperty().addListener(obs->{
filter(filteredData);
});
}
private void filter(FilteredList<PurchaseOrder> filteredData)
{
filteredData.setPredicate(PurchaseOrder -> {
// If all filters are empty then display all Purchase Orders
if ((txtFilter.getText() == null || txtFilter.getText().isEmpty())
&& (txtFilter2.getText() == null || txtFilter2.getText().isEmpty())) {
return true;
}
// Convert filters to lower case
String lowerCaseFilter = txtFilter.getText().toLowerCase();
String lowerCaseFilter2 = txtFilter2.getText().toLowerCase();
//If fails any given criteria, fail completely
if(txtFilter.getText().length()>0)
if (PurchaseOrder.get("vendor_name").toLowerCase().contains(lowerCaseFilter) == false)
return false;
if(txtFilter2.getText().length()>0)
if (PurchaseOrder.get("PONumber").toLowerCase().contains(lowerCaseFilter2) == false)
return false;
return true; // Matches given criteria
});
}
I'm using JBoss' Resteasy as our JAX-RS provider. We have a requirement to read the servlet request body for authentication purpose, the problem is once the InputStream is read in the request, it cannot be read again, hence #FormParam won't work unless I can somehow "put the content back". I've tried the following two options:
Using Resteasy's PreProcessInterceptor, I was able to read the body, but there's no way to reset the InputStream or add a wrapper type. The documentation doesn't mention anything about this. According to JBoss' issue tracker, it's not currently possible.
Using the Servlet filter + Wrapper type apporach (see example here), I was able to get the request body in #javax.ws.rs.core.Context HttpServletRequest request but all the #FormParam still return null.
Here's a snippet of the PreProcessorInterceptor:
#Provider
#ServerInterceptor
public class SomePreprocessor implements PreProcessInterceptor {
public ServerResponse preProcess(HttpRequest request, ResourceMethod method)
throws Failure, WebApplicationException {
try{
StringWriter writer = new StringWriter();
IOUtils.copy(request.getInputStream(), writer, "UTF-8");
System.out.println("Request Body: "+writer.toString());
// What can I do to reset the request body?
}
catch(Exception ex){
ex.printStackTrace();
}
return null;
}
}
Here's a snippet of the rest method:
#POST
#Path("/something")
#Produces("application/xml")
public Response doSomething(
#FormParam("name") String name,
#javax.ws.rs.core.Context HttpServletRequest request) {
// name is always null
System.out.println(name);
// prints nothing in approach 1, returns the body in approach 2
java.io.StringWriter writer = new java.io.StringWriter();
org.apache.commons.io.IOUtils.copy(request.getReader(), writer);
System.out.println(writer.toString());
}
If anyone is still interested in the answer, here's how I solved it:
Create a custom type that extends HttpServletRequestWrapper. Make sure you override
getInputStream()
getReader()
getParameter()
getParameterMap()
getParameterNames()
getParameterValues()
This is because when Resteasy tries to bind using #Form, #FormParam, and #QueryParam etc, it calls the getParameter() method on the Resteasy class, which is then delegated to the underlying request, in my case, Apache's Coyote Servlet Request. So overriding getInputStream() and getReader() alone are not enough, you must make sure that getParameter() utilize the new input stream as well.
If you want to store the body for later use, you must then construct the param map yourself by parsing the query string and url-encoded form body. It's quite straight forward to implement but it carries its own risk. I recommend reading Coyote's implementation of the same methods.
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URLDecoder;
import java.util.Collections;
import java.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.ws.rs.core.MediaType;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
/**
* Wrapper class that supports repeated read of the request body and parameters.
*/
public class CustomHttpServletRequest extends HttpServletRequestWrapper {
private static final Logger logger = Logger.getLogger(CustomHttpServletRequest.class);
// A typical url encoded form is "key1=value&key2=some%20value"
public final static Pattern urlStrPattern = Pattern.compile("([^=&]+)=([^&]*)[&]?");
// Cached request body
protected ByteArrayOutputStream cachedBytes;
protected String encoding;
protected String requestBody;
// Cached form parameters
protected Map<String, String[]> paramMap = new LinkedHashMap<String, String[]>();
// Cached header names, including extra headers we injected.
protected Enumeration<?> headerNames = null;
/**
*
* #param request
*/
public CustomHttpServletRequest(HttpServletRequest request) {
super(request);
// Read the body and construct parameters
try{
encoding = (request.getCharacterEncoding()==null)?"UTF-8":request.getCharacterEncoding();
// Parameters in query strings must be added to paramMap
String queryString = request.getQueryString();
logger.debug("Extracted HTTP query string: "+queryString);
if(queryString != null && !queryString.isEmpty()){
addParameters(queryString, encoding);
}
// Parse the request body if this is a form submission. Clients must set content-type to "x-www-form-urlencoded".
requestBody = IOUtils.toString(this.getInputStream(), encoding);
if (StringUtils.isEmpty(requestBody)) {requestBody = null;}
logger.debug("Extracted HTTP request body: "+requestBody);
if(request.getContentType() != null && request.getContentType().toLowerCase().contains(MediaType.APPLICATION_FORM_URLENCODED)){
addParameters(requestBody, encoding);
}
}
catch(IOException ex){
throw new RuntimeException(ex);
}
}
/**
*
* #param requestBody
* #param encoding
* #throws IOException
*/
private void addParameters(String requestBody, String encoding) throws IOException {
if(requestBody == null){
return;
}
Matcher matcher = urlStrPattern.matcher(requestBody);
while(matcher.find()){
String decodedName = URLDecoder.decode(matcher.group(1), encoding);
// If there's no value, matcher.group(2) returns an empty string instead of null
String decodedValue = URLDecoder.decode(matcher.group(2), encoding);
addParameter(decodedName, decodedValue);
logger.debug("Parsed form parameter, name = "+decodedName+", value = "+decodedValue);
}
}
/**
*
* #param name
* #param value
*/
private void addParameter(String name, String value) {
String[] pv = paramMap.get(name);
if (pv == null) {
pv = new String[]{value};
paramMap.put(name, pv);
}
else {
String[] newValue = new String[pv.length+1];
System.arraycopy(pv, 0, newValue, 0, pv.length);
newValue[pv.length] = value;
paramMap.put(name, newValue);
}
}
/*
* (non-Javadoc)
* #see javax.servlet.ServletRequestWrapper#getInputStream()
*/
#Override
public ServletInputStream getInputStream() throws IOException {
if (cachedBytes == null){
cachedBytes = new ByteArrayOutputStream();
IOUtils.copy(super.getInputStream(), cachedBytes);
}
// Return a inner class that references cachedBytes
return new CachedServletInputStream();
}
/*
* (non-Javadoc)
* #see javax.servlet.ServletRequestWrapper#getReader()
*/
#Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
/**
*
* #return
*/
public String getRequestBody() {
return requestBody;
}
/*
* (non-Javadoc)
* #see javax.servlet.ServletRequestWrapper#getParameter(java.lang.String)
*/
#Override
public String getParameter(String name) {
if(paramMap.containsKey(name)){
String[] value = (String[]) paramMap.get(name);
if(value == null){
return null;
}
else{
return value[0];
}
}
return null;
}
/*
* (non-Javadoc)
* #see javax.servlet.ServletRequestWrapper#getParameterMap()
*/
#Override
public Map<String, String[]> getParameterMap() {
return Collections.unmodifiableMap(paramMap);
}
/*
* (non-Javadoc)
* #see javax.servlet.ServletRequestWrapper#getParameterNames()
*/
#Override
public Enumeration<?> getParameterNames() {
return Collections.enumeration(paramMap.keySet());
}
/*
* (non-Javadoc)
* #see javax.servlet.ServletRequestWrapper#getParameterValues(java.lang.String)
*/
#Override
public String[] getParameterValues(String name) {
if(paramMap.containsKey(name)){
return paramMap.get(name);
}
return null;
}
/**
* Inner class that reads from stored byte array
*/
public class CachedServletInputStream extends ServletInputStream {
private ByteArrayInputStream input;
public CachedServletInputStream() {
input = new ByteArrayInputStream(cachedBytes.toByteArray());
}
#Override
public int read() throws IOException {
return input.read();
}
#Override
public int read(byte[] b) throws IOException {
return input.read(b);
}
#Override
public int read(byte[] b, int off, int len) {
return input.read(b, off, len);
}
}
}
And add a filter to wrap the original request:
public class CustomFilter implements Filter {
private static final Logger logger = Logger.getLogger(CustomFilter.class);
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
if(request!=null && request instanceof HttpServletRequest){
HttpServletRequest httpRequest = (HttpServletRequest) request;
logger.debug("Wrapping HTTP request");
request = new CustomHttpServletRequest(httpRequest);
}
chain.doFilter(request, response);
}
}
I have question/issue with javascript function binding in Spring MVC . As per our requirement I need to insert a NEW ROW in a table when the user clicks on “ADD” button .
Step1 : So when the user clicks on “Add MORE” button I inserting a new row within a table , I am handling this using javascript
Step 2: When user clicks on the submit button , I Need to send the values entered by user to my Controller (Spring MVC Controller) .
So how can binding the values to controller dynamically ?
Please help me to resolve this issue ASAP .
I do the following when I need to bind a dynamic list of objects coming from the front end :
Post the data as a json array, i.e. in the following format
{ data : [{a:1, b:2}, {a:3, b:4}] }
In the Controller
#RequestMapping(value="save", method=RequestMethod.POST)
public void save(JSONObject object)
{
List<YourType> list = new ArrayList<YourType>();
JSONArray array = object.getJSONArray("data")
for(int i=0; i<array.length(); i++)
{
//getObjectFromJson is your method for converting json to an object of your type.
list.add(JsonUtils.fromJson(array.getJSONObject(i).toString(), YourType.class);
}
}
Spring can bind maps and lists of objects if you give create an appropriate class to hold your form data then use the #ModelAttribute annotation on your controller method.
For example, if your JavaScript creates table rows like this:
<tr>
<td><input name="bells[0]" /></td>
<td><input name="whistles[0]" /></td>
</tr>
<tr>
<td><input name="bells[1]" /></td>
<td><input name="whistles[1]" /></td>
</tr>
Then you can create a model class that contains a list for each repeating field in your HTML form, like this:
public class AsapForm {
private List<String> bells;
private List<String> whistles;
// add getters and setters here
}
And then you can create a controller method that uses that class as a parameter with the #ModelAttribute annotation:
public void postAsapForm(#ModelAttribute("contactForm") AsapForm asapForm, BindingResult result) {
...
}
You can then access the values for each row using asapForm.getBells() and asapForm.getWhistles() etc.
I have achieved this using LazyList.
You need to do this in following way.
Operation.java
package com.xxx.xxx.model;
// Generated Feb 9, 2012 11:30:06 AM by Hibernate Tools 3.2.1.GA
import java.util.ArrayList;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import org.apache.commons.collections.FactoryUtils;
import org.apache.commons.collections.list.LazyList;
#Entity
#Table(name="Operations"
,schema="dbo"
)
public class Operations implements java.io.Serializable {
private int operationId;
#Embedded
private Services services;
private String operationName;
private String isHqlsql;
private String isMultipleTables;
private String listOfTablesAffected;
private String hqlQuery;
private String typeOfOperation;
private String operationDetail;
private String inputVariables;
private String outputparamdatatype;
private String isCountQuery;
private String isDynamicWhereQry;
private String isPaginationRequired;
private String biInputParameters;
private List<OperationParameters> operationParameterses = LazyList
.decorate(new ArrayList<OperationParameters>(),
FactoryUtils.instantiateFactory(OperationParameters.class));
public Operations() {
}
public Operations(int operationId, Services services, String operationName) {
this.operationId = operationId;
this.services = services;
this.operationName = operationName;
}
public Operations(int operationId, Services services, String operationName, String isHqlsql, String isMultipleTables, String listOfTablesAffected, String hqlQuery, String typeOfOperation, String operationDetail, String inputVariables, String outputparamdatatype, String isCountQuery, List operationParameterses) {
this.operationId = operationId;
this.services = services;
this.operationName = operationName;
this.isHqlsql = isHqlsql;
this.isMultipleTables = isMultipleTables;
this.listOfTablesAffected = listOfTablesAffected;
this.hqlQuery = hqlQuery;
this.typeOfOperation = typeOfOperation;
this.operationDetail = operationDetail;
this.inputVariables = inputVariables;
this.outputparamdatatype = outputparamdatatype;
this.isCountQuery = isCountQuery;
this.operationParameterses = operationParameterses;
}
#Id
#GeneratedValue
#Column(name="operationId", unique=true, nullable=false)
public int getOperationId() {
return this.operationId;
}
public void setOperationId(int operationId) {
this.operationId = operationId;
}
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="serviceId", nullable=false)
public Services getServices() {
return this.services;
}
public void setServices(Services services) {
this.services = services;
}
#Column(name="operationName", nullable=false, length=250)
public String getOperationName() {
return this.operationName;
}
public void setOperationName(String operationName) {
this.operationName = operationName;
}
#Column(name="isHQLSQL", length=50)
public String getIsHqlsql() {
return this.isHqlsql;
}
public void setIsHqlsql(String isHqlsql) {
this.isHqlsql = isHqlsql;
}
#Column(name="isMultipleTables", length=50)
public String getIsMultipleTables() {
return this.isMultipleTables;
}
public void setIsMultipleTables(String isMultipleTables) {
this.isMultipleTables = isMultipleTables;
}
#Column(name="listOfTablesAffected", length=500)
public String getListOfTablesAffected() {
return this.listOfTablesAffected;
}
public void setListOfTablesAffected(String listOfTablesAffected) {
this.listOfTablesAffected = listOfTablesAffected;
}
#Column(name="hqlQuery")
public String getHqlQuery() {
return this.hqlQuery;
}
public void setHqlQuery(String hqlQuery) {
this.hqlQuery = hqlQuery;
}
#Column(name="typeOfOperation", length=50)
public String getTypeOfOperation() {
return this.typeOfOperation;
}
public void setTypeOfOperation(String typeOfOperation) {
this.typeOfOperation = typeOfOperation;
}
#Column(name="operationDetail")
public String getOperationDetail() {
return this.operationDetail;
}
public void setOperationDetail(String operationDetail) {
this.operationDetail = operationDetail;
}
#Column(name="inputVariables", length=5000)
public String getInputVariables() {
return this.inputVariables;
}
public void setInputVariables(String inputVariables) {
this.inputVariables = inputVariables;
}
#Column(name="outputparamdatatype", length=50)
public String getOutputparamdatatype() {
return this.outputparamdatatype;
}
public void setOutputparamdatatype(String outputparamdatatype) {
this.outputparamdatatype = outputparamdatatype;
}
#Column(name="isCountQuery", length=10)
public String getIsCountQuery() {
return this.isCountQuery;
}
public void setIsCountQuery(String isCountQuery) {
this.isCountQuery = isCountQuery;
}
public String getIsDynamicWhereQry() {
return isDynamicWhereQry;
}
public void setIsDynamicWhereQry(String isDynamicWhereQry) {
this.isDynamicWhereQry = isDynamicWhereQry;
}
public String getIsPaginationRequired() {
return isPaginationRequired;
}
public void setIsPaginationRequired(String isPaginationRequired) {
this.isPaginationRequired = isPaginationRequired;
}
public String getBiInputParameters() {
return biInputParameters;
}
public void setBiInputParameters(String biInputParameters) {
this.biInputParameters = biInputParameters;
}
#OneToMany(cascade=CascadeType.ALL, fetch=FetchType.LAZY, mappedBy="operations")
public List<OperationParameters> getOperationParameterses() {
return this.operationParameterses;
}
public void setOperationParameterses(List<OperationParameters> operationParameterses) {
this.operationParameterses = operationParameterses;
}
}
OperationParameters.java
package com.xxx.xxx.model;
// Generated Feb 9, 2012 11:30:06 AM by Hibernate Tools 3.2.1.GA
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
#Entity
#Table(name="OperationParameters"
,schema="dbo"
)
public class OperationParameters implements java.io.Serializable {
private int parameterId;
private Operations operations;
private String inputOutputParamName;
private String inputOutputParamType;
private String inputOutputParamDataType;
public OperationParameters() {
}
public OperationParameters(int parameterId, Operations operations, String inputOutputParamName, String inputOutputParamType, String inputOutputParamDataType) {
this.parameterId = parameterId;
this.operations = operations;
this.inputOutputParamName = inputOutputParamName;
this.inputOutputParamType = inputOutputParamType;
this.inputOutputParamDataType = inputOutputParamDataType;
}
#Id
#GeneratedValue
#Column(name="parameterId", unique=true, nullable=false)
public int getParameterId() {
return this.parameterId;
}
public void setParameterId(int parameterId) {
this.parameterId = parameterId;
}
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="operationId", nullable=false)
public Operations getOperations() {
return this.operations;
}
public void setOperations(Operations operations) {
this.operations = operations;
}
#Column(name="inputOutputParamName", nullable=false, length=250)
public String getInputOutputParamName() {
return this.inputOutputParamName;
}
public void setInputOutputParamName(String inputOutputParamName) {
this.inputOutputParamName = inputOutputParamName;
}
#Column(name="inputOutputParamType", nullable=false, length=250)
public String getInputOutputParamType() {
return this.inputOutputParamType;
}
public void setInputOutputParamType(String inputOutputParamType) {
this.inputOutputParamType = inputOutputParamType;
}
#Column(name="inputOutputParamDataType", nullable=false, length=250)
public String getInputOutputParamDataType() {
return this.inputOutputParamDataType;
}
public void setInputOutputParamDataType(String inputOutputParamDataType) {
this.inputOutputParamDataType = inputOutputParamDataType;
}
}
Conroller method to serve the post request to add new Operation.
/**
* Method that will serve the post request to add the operation and operation parameters submitted by the user.
* #param operations
* #param map
* #return {#link String} The view name that will redirect to the get request to display the previous page with newly entered operation in the list.
*/
#RequestMapping(value="/add", method=RequestMethod.POST)
public String addOperations(#ModelAttribute Operations operations, ModelMap map) {
operations.getOperationParameterses().removeAll(Collections.singleton(null));
for(int i=0; i<operations.getOperationParameterses().size(); i++) {
System.out.println("parameterName :: " + ((OperationParameters)operations.getOperationParameterses().get(i)).getInputOutputParamName());
if(((OperationParameters)operations.getOperationParameterses().get(i)).getInputOutputParamName() == null || "".equalsIgnoreCase((((OperationParameters)operations.getOperationParameterses().get(i))).getInputOutputParamName())) {
operations.getOperationParameterses().remove(i);
System.out.println("empty parameter removed....");
}
}
return "redirect:/operations/" + operations.getServices().getServiceId();
}
Hope this helps you.
I am trying to make a class that staggers the net connections calls by a certain amount to not put too much pressure on my server, and so I don't have a dozen net connectors running around in my code.
I want a class that I can send a call command to, the class adds the call to the queue, and then about every one second it see if anything is in the queue, and if so calls it. This is what I have so far.
package net
{
import flash.events.TimerEvent;
import flash.net.NetConnection;
import flash.net.Responder;
import flash.utils.Timer;
public class Server
{
private static var gateway:String = "http://localhost/gateway.php";
private static var queue:Vector.<ServerNode>
private static var nc:NetConnection;
private static var instance:Server = null;
private static var res:Responder;
private var timer:Timer;
public function Server(e:ServerEnforcer) {
nc = new NetConnection();
queue = new Vector.<ServerNode>();
timer = new Timer(1000);
timer.addEventListener(TimerEvent.TIMER, execCall);
timer.start();
}
public static function getInstance():Server {
if (instance == null) {
instance = new Server(new ServerEnforcer);
}
return instance;
}
private function execCall(e:Event):void {
if (queue.length > 0) {
var node:ServerNode = queue.pop();
nc.call(node.method, node.res, node.args);
}
}
public function call(method:String, success:Function, failure:Function, ...args):void {
queue.unshift(new ServerNode(method, success, failure, args));
}
private function serverFailure(event:Object):void {
trace("Server Failure : " + event.description);
}
}
}
import flash.net.Responder;
class ServerEnforcer { }
class ServerNode {
public var method:String;
public var success:Function;
public var failure:Function;
public var args:Array;
public var res:Responder
public function ServerNode(_method:String, _success:Function, _failure:Function, _args:Array) {
method = _method;
success = _success;
failure = _failure;
res = new Responder(success, failure);
args = _args;
}
}
Now when I call
Server.getInstance().call("Fetch.getData", parseAllData, onError)
public function parseAllData(event:Object):void {
trace("Victory!");
}
public function onError(event:Object):void {
trace("Error :" + event);
}
absolutely nothing happens. Any idea why or a point in the right direction why this isn't working?
You created an instance of the NetConnection, but haven't actually initiated a connection with the server.
In other words,
nc.connect(gateway);
is missing.
See NetConnection documentation for more information on that class.