Threadsafety in SwingWorker - Updating JTable in a Threadsafe way - java

I have a list of travel offers that I read and parsed from a XML file and added them to my GUI using JTable. I also have some update functionalities (at interval and instantly on click) that updates the GUI as soon as new offers are added to the XML. My aim is to add the offers in the GUI in thread safe way.
This is the class (UpdateData.java) where i perform doInBackground() using Swingworker and more concern about safety. (Other classes are also shown below if anyone is interested to take a deeper look) Can SwingUtilities.invokeLater() be used to make it thread-safe? Does overriding Swingworkers done(), execute() and process() will help in some way to achieve safety? In that case how? (newbie at thread prog) (Other classes are given below if anyone is interested to get a deeper look). Some Help / Feedback will be highly appreciated.
Class: UpdateData.java
public class UpdateData extends SwingWorker<Integer, Integer> {
private ArrayList<RawTravelData> listOfOffer;
private TravelData offerData;
private XMLReader parseData;
//the controller
private ControlUpdate updtController;
//constructor
public UpdateData(TravelData o, ControlUpdate offerController) {
updtController = offerController;
parseData = new XMLReader();
offerData = o;
}
#Override
protected Integer doInBackground() throws Exception {
listOfOffer = parseData.fetchData();
offerData.setData(listOfOffer);
updtController.setOfferArray(listOfOffer);
return null;
}
}
Class: RawTravelData.java
public class RawTravelData {
private String destination = "";
private String travelDate = "";
private int currPrice;
//empty constructor
public RawTravelData() {
}
//setters ad getters for destination, travel date and currprise
}
Class: TravelData.java
public class TravelData extends AbstractTableModel {
//the table header strings
private String[] colNames = { "Destination", "Date", "Price", "Details" };
private static final long serialVersionUID = 1L;
//arraylist of the offer data
private ArrayList<RawTravelData> offerList;
//constructor
public TravelData(ArrayList<RawTravelData> rtd) {
offerList = rtd;
}
//second constructor to create empty list
public TravelData() {
offerList = new ArrayList<RawTravelData>();
}
//add the list
public void setData(ArrayList<RawTravelData> o) {
offerList = o;
this.fireTableDataChanged();
}
//get the offer list
public ArrayList<RawTravelData> getOfferList() {
return offerList;
}
#Override
public Class<?> getColumnClass(int columnIndex) {
switch (columnIndex) {
case 0:
return String.class;
case 1:
return Integer.class;
case 2:
return String.class;
case 3:
return String.class;
default:
break;
}
return String.class;
}
#Override
public int getColumnCount() {
return colNames.length;
}
#Override
public int getRowCount() {
return offerList.size();
}
#Override
public Object getValueAt(int arg0, int arg1) {
switch (arg1) {
case 0:
return offerList.get(arg0).getDestination();
case 1:
return offerList.get(arg0).getPrice();
case 2:
return offerList.get(arg0).getTravelDate();
case 3:
return "Details";
default:
break;
}
return "null";
}
#Override
public String getColumnName(int col) {
return colNames[col];
}
}
Class: XMLReader.java
public class XMLReader {
//Method to fetch and read all the data from the XML file
public ArrayList<RawTravelData> fetchData() {
//parse data and return as arraylist of offers
return arrayOfOffer;
}
}
Class: ControlUpdate.java
//This class is responsible for controlling the updating of the offer data in the background
public class ControlUpdate {
private TablePanel tablePane;
private ArrayList<RawTravelData> offerArray;
//..
//Constructor
public ControlUpdate(TablePanel tablePane) {
settingsVal = new SaveSettings();
this.tablePane = tablePane;
tablePane.getOfferTable().addMouseListener(
new TableSortListener(tablePane.getOfferTable(), this));
runUpdateTask();
setUpdateInterval(settingsVal.readSettings());
}
//run the updates
private void runUpdateTask() {
//used Timer and ScheduledThreadPool
}
//get the table panel
public TablePanel getTablePanel() {
return tablePane;
}
//setting the list to a new offer list for the updater
public void setOfferArray(ArrayList<RawTravelData> rtd) {
offerArray = rtd;
}
}

All modifications of Components and their models need to be performed in the AWT event dispatch thread, not in a background thread. The second and third lines of your doInBackground method should be moved to the done method, which is guaranteed to be executed in the AWT event thread.
It is also customary to have the SwingWorker's value type be the data you're obtaining in the background.
public class UpdateData
extends SwingWorker<List<RawTravelData>, Integer> {
// ...
#Override
protected List<RawTravelData> doInBackground() throws Exception {
return parseData.fetchData();
}
#Override
protected void done() {
try {
List<RawTravelData> listOfOffer = get();
offerData.setData(listOfOffer);
updtController.setOfferArray(listOfOffer);
} catch (ExecutionException e) {
throw new RuntimeException(e);
} catch (InterruptedException e) {
// Someone wants us to exit cleanly.
e.printStackTrace();
}
}
}

Related

Java return type generic

I'm newbie in using generic in java, i have simple method that i would like to return any type from that, this method return other class getters methods, for example i have channels class and that have getChannelId() method, i want to return that from this method:
#SuppressWarnings("unchecked")
public <T extends Channels> T getChannelColumn(ChannelContentModel.channelColumns column) {
switch (column) {
case id:
return (T) channel.getId();
break;
case title:
return (T) channel.getChannelTitle();
break;
}
return null;
}
getId is int and getChannelTitle is string, how can i fix this method to return any type?
in my code channel is instance of Channels class, Thanks in advance
As commented by #Geckstar;
Java is statically typed
But it is possible to clear your code sample from compile errors.
Remove breaks, because there is no need to break after return statement.
extend return type from object. T extends Object instead T extends Channel
id must be Integer, not int or change to (T)(Integer) channel.getId()
This is not a good solution, it only fixes compile errors.But this method's usage may cause runtime failures.
#SuppressWarnings("unchecked")
public <T extends Object> T getChannelColumn(ChannelContentModel.channelColumns column) {
switch (column) {
case id:
return (T) channel.getId(); //int must be Integer
case title:
return (T) channel.getChannelTitle();
}
return null;
}
You can create property wrapper which will hold the value in string data format when required it will convert to a specific data type.
public class MyProperty {
private String stringVal;
public MyProperty(String stringVal) {
this.stringVal = stringVal;
}
public Integer getInteger() throws IllegalArgumentException {
return integerValue.getValue();
}
public String getString() {
return stringValue.getValue();
}
private PropertyValue<String> stringValue = new PropertyValue<String>() {
protected String parse(String rep) {
return rep;
}
};
private PropertyValue<Integer> integerValue = new PropertyValue<Integer>() {
protected Integer parse(String rep) throws NumberFormatException {
return Integer.valueOf(rep);
}
};
private abstract class PropertyValue<T> {
private T value;
protected abstract T parse(String rep) throws Exception;
public T getValue() throws IllegalArgumentException {
try {
value = (stringValue == null) ? null : parse(stringVal);
} catch (Exception e) {
value = null;
}
return value;
}
}
}
you can make specialize class or one class having the access method to get the value with specified data type
#SuppressWarnings("unchecked")
public MyProperty getChannelColumn(ChannelContentModel.channelColumns column) {
switch (column) {
case id:
return new MyProperty(channel.getId());//
break;
case title:
return new MyProperty(channel.getChannelTitle());
break;
}
return null;
}

For loops and too many if else how to test

I have a code which goes like this
List insert;
List update;
List delete
for(SomeObject someObj : someObjects){
if(isNew){
insert.add()
}
else if(isUpdate){
update.add();
}
if(isDelete){
delete.add()
}
}
//call update insert delete functions
The problem is this code is untestable because the update insert delete are all void methods.
My question is , should I consider iterating over the loop three times and then get the lists to test if the logic to filter each type of results is working? The cost is not that much since I am expecting <100 elements in the list.
You can check
(sum of lists sizes after) == (sum of lists sizes before) + someObjects.size();
The key here is to be able to control the dependencies and access the state that this method acts upon.
Here is an example that illustrates that:
interface SomeDao {
void add(SomeObject object);
void update(SomeObject object);
void delete(SomeObject object);
}
class SomeDaoStub implements SomeDao {
#Override public void add(SomeObject object) {}
#Override public void update(SomeObject object) {}
#Override public void delete(SomeObject object) {}
}
class SomeObject {
private final boolean isNew;
private final boolean isUpdated;
private final boolean isDeleted;
SomeObject(boolean isNew, boolean isUpdated, boolean isDeleted) {
this.isNew = isNew;
this.isUpdated = isUpdated;
this.isDeleted = isDeleted;
}
public boolean isNew() {
return isNew;
}
public boolean isUpdated() {
return isUpdated;
}
public boolean isDeleted() {
return isDeleted;
}
}
public void doSomethingComplicatedWithListsInAForLoop(Iterable<SomeObject> someObjects, SomeDao dao) {
for (SomeObject someObject : someObjects) {
if (someObject.isNew()) {
dao.add(someObject);
} else if (someObject.isUpdated()) {
dao.update(someObject);
} else if (someObject.isDeleted()) {
dao.delete(someObject);
}
}
}
#Test
public void itDeletesObjectsMarkedToBeDeleted() {
final List<SomeObject> actualDeletedObjects = new ArrayList<>();
List<SomeObject> expectedDeletedObjects = Arrays.asList(
new SomeObject(false, false, true),
new SomeObject(false, false, true),
new SomeObject(false, false, true)
);
SomeDao theDao = new SomeDaoStub() {
#Override
public void delete(SomeObject object) {
actualDeletedObjects.add(object);
}
};
doSomethingComplicatedWithListsInAForLoop(expectedDeletedObjects, theDao);
assertEquals(expectedDeletedObjects, actualDeletedObjects);
}
The only reason that I can figure out what doSomethingComplicatedWithListsInAForLoop manipulated is because I can control its dependencies, namely, in this example, SomeDao.
It is likely that you are finding difficulty testing your method because it makes calls to state which you cannot inject.

RxAndroid. Simple caching and filtering data

I read a ton of literature about the Rx and, on the one hand, everything is clear, but on the other hand nothing is clear. I'm trying to do a simple filtration and storage of data from the server with this library, and it is not working as expected. I need to implement a simple channel list management:
1. Cache on disk and in memory
2. When user requested - return filtered channels
3. All Subscribers that was attached to this Observable must be notified if filtered cahnnels list was changed (call onNext() for all Subscribers)
I wrote the following:
ArrayList<Channel> channels = null;
ArrayList<Channel> filteredChannels = null;
Observable<ArrayList<Channel>> filteredObservable = Observable.create()
// here I need to check if cache is valid, if no - download from server
// then filter channels and return filteredChannels in onNext call
// all subscribers that called subscribe before and not called unsubscribe must be notified if filteredChannels changed
// how to implement this?
.subscribeOn(Schedulers.io());
public void setFilter(Filter filter) {
// update filteredChannels with the new filter and notify all Subscribers (call onNext()). How to implement this?
}
public Observable<ArrayList<Channel>> getFilteredChannels() {
return filteredObservable;
}
Do I correctly understood logic of the Rx pattern or not? Thanks in advance.
I would use .map()'s function to filter your list and then return your desired output. This would mean that your Observable would be emitting the filtered results.
Then you could commit the filtered list to your cache in your .subscribe() functions.
I found the solution for this task: I need to use flatMap and map and I tried to use BehaviourSubject to notify all subscribers for variable change, but this solution is not stable at all, because of error MissingBackpressureException, that's why I wrote next ObsevableValue class:
public class ObservableValue<T> {
private static final String TAG = ObservableValue.class.getSimpleName();
private ArrayList<Registration> subscriptions = new ArrayList<>();
private T value;
private int index;
private boolean firstNotify;
private ReentrantLock lock = new ReentrantLock();
public ObservableValue() {
firstNotify = false;
}
public ObservableValue(T defaultValue) {
value = defaultValue;
firstNotify = true;
}
#SuppressWarnings("unchecked")
public void setValue(T value) {
lock.lock();
this.value = value;
for (Registration listener: subscriptions) {
if (!listener.isFinished()) {
listener.getListener().valueChanged(value);
}
}
lock.unlock();
}
public T getValue() {
lock.lock();
T result = value;
lock.unlock();
return result;
}
public Registration subscribe(ValueChangeListener<T> listener) {
lock.lock();
Registration s = new Registration(this, index, listener);
index++;
subscriptions.add(s);
if (firstNotify||value!=null) {
listener.valueChanged(value);
}
lock.unlock();
return s;
}
protected void finish(int index) {
lock.lock();
for (int i=0;i<subscriptions.size();i++) {
Registration s = subscriptions.get(i);
if (s.getIndex()==index) {
subscriptions.remove(i);
break;
}
}
lock.unlock();
}
}
public abstract class ValueChangeListener<T> {
private Looper looper;
public ValueChangeListener() {}
public ValueChangeListener(Looper looper) {
this.looper = looper;
}
public void valueChanged(T data) {
if (looper!=null) {
Handler handler = new Handler(looper, msg -> {
onValueChanged(data);
return true;
});
handler.sendEmptyMessage(0);
} else {
onValueChanged(data);
}
}
public abstract void onValueChanged(T data);
}
public class Registration {
private ObservableValue observableValue;
private int index;
private ValueChangeListener listener;
private volatile boolean finished = false;
protected Registration(ObservableValue observableValue, int index, ValueChangeListener listener) {
this.observableValue = observableValue;
this.index = index;
this.listener = listener;
}
protected ValueChangeListener getListener() {
return listener;
}
protected int getIndex() {
return index;
}
public boolean isFinished() {
return finished;
}
public void finish() {
finished = true;
observableValue.finish(index);
}
}
Now it is working as expected. Like in Rx ObservableValue returns a Registration object that need to be finished() when it is not needed anymore.

How to couple an instance member to a instance method?

I have a class structure like :
public interface DBReader {
public Map<String, String> read(String primaryKey, String valueOfPrimaryKey,
boolean scanIndexForward, boolean consistentRead, int maxPageSize);
public int getA(String ___);
public int getB(String ___);
public int getC(String ___);
}
public class DynamoDBReader implements DBReader {
private DynamoDB dynamoDB;
private String tableName;
private Table table;
private int throughput;
private DynamoDBReader(Builder builder) {
this.throughput = builder.throughput;
this.tableName = builder.tableName;
this.dynamoDB = builder.dynamoDB;
this.table = dynamoDB.getTable(builder.tableName);
if (table == null) {
throw new InvalidParameterException(String.format("Table %s doesn't exist.", tableName));
}
}
#Override
public int getA(String ____) {
read(_________);
}
return ________;
}
#Override
public int getB(String ____) {
read(_________);
}
return ________;
}
#Override
public int getC(String ____) {
read(_________);
}
return ________;
}
#Override
public Map<String, String> read(String primaryKey, String valueOfPrimaryKey, boolean scanIndexForward,
boolean consistentRead, int maxPageSize) {
QuerySpec spec = new QuerySpec()
.withHashKey(primaryKey, valueOfPrimaryKey)
.withScanIndexForward(scanIndexForward)
.withConsistentRead(consistentRead)
.withMaxPageSize(maxPageSize);
ItemCollection<QueryOutcome> items = table.query(spec);
Iterator<Item> itemIterator = items.firstPage().iterator();
Map<String, String> itemValues = new HashMap<String, String>();
while (itemIterator.hasNext()) {
Item item = itemIterator.next();
}
return itemValues;
}
}
#VisibleForTesting
protected void setTable(Table table) {
this.table = table;
}
/**
* Returns a new builder.
*/
public static Builder builder() {
return new Builder();
}
public static class Builder {
private String tableName;
private int throughput;
private DynamoDB dynamoDB;
private Builder() { }
public Builder tableName(String tableName) {
this.tableName = tableName;
return this;
}
public Builder throughput(int throughput) {
this.throughput = throughput;
return this;
}
public Builder dynamoDB(DynamoDB dynamoDB) {
this.dynamoDB = dynamoDB;
return this;
}
public DynamoDBReader build() {
if (tableName == null) {
throw new InvalidParameterException("Table name can't be null.");
}
if (throughput <= 0) {
throw new InvalidParameterException("Throughput should be > 0.");
}
if (dynamoDB == null) {
throw new InvalidParameterException("dynamoDB can't be null.");
}
return new DynamoDBReader(this);
}
}
}
Problem : getA(), getB(), getC() are only valid for specific tableNames. For a table getA() is Valid but getB() and getC() wont make any sense.
How to couple method names with table name so that someone with a table name knows which function is valid.
Solution to create subclasses for different getters doesn't look a great idea to me.
Solution to create subclasses for different getters doesn't look a great idea to me.
Can you please elaborate why?
I hear that all the time, 'I don't like it...', 'This seems ugly...', 'It shouldn't do that'. Reasons for not liking a particular solution should be backed by objective reasons, not personal opinions. Most of the time our intuition as developers tells us that something is wrong when it is actually violating some software development principle. But sometimes it is just plain old personal feeling without any particular logical reason. When that happens I like to get to specifics.
Your solution violates a basic software principle called SRP.
Having table modules will be much better solution.

Can I get rid of this switch and enum?

I am under the impression that this piece of code could be cleaner by somehow utilizing polymorphism, but I can't seem to find a proper way of doing it. I tried using the Visitor pattern but didn't manage to get very far with it.
The "Hero" class that has the switch:
public class Hero {
private Equipment equipment = new Equipment();
// other fields
public void equipArmor(Armor armor) {
findCorrespondingArmorSlot(armor).equipItem(armor);
}
private ItemSlot findCorrespondingArmorSlot(Armor armor) {
switch (armor.getArmorType()) {
case SHIELD:
return equipment.offHand;
case BODY:
return equipment.body;
case HEAD:
return equipment.head;
case GLOVES:
return equipment.hands;
case BOOTS:
return equipment.feet;
case BELT:
return equipment.waist;
case AMULET:
return equipment.neck;
case RING:
return equipment.finger;
case TRINKET:
return equipment.special;
}
throw new NullPointerException();
}
public Equipment getEquipment() {
return equipment;
}
// other methods
public class Equipment {
public ItemSlot mainHand = new ItemSlot();
public ItemSlot offHand = new ItemSlot();
public ItemSlot body = new ItemSlot();
public ItemSlot head = new ItemSlot();
public ItemSlot hands = new ItemSlot();
public ItemSlot feet = new ItemSlot();
public ItemSlot waist = new ItemSlot();
public ItemSlot neck = new ItemSlot();
public ItemSlot finger = new ItemSlot();
public ItemSlot special = new ItemSlot();
}
}
And some other stuff:
public class ItemSlot {
private static final Miscellaneous EMPTY = new Miscellaneous();
private Item item = EMPTY;
public Item getItem() {
return item;
}
public void equipItem(Item item) {
unequipItem();
this.item = item;
}
public void unequipItem() {
if (!isEmpty()) {
item.addToInventory();
item = EMPTY;
}
}
public boolean isEmpty() {
return (item == EMPTY);
}
}
public abstract class Item {
// fields
public void addToInventory() {
// code
}
// other methods
}
public class Miscellaneous extends Item{}
public class Armor extends Item {
private ArmorType type;
public ArmorType getArmorType() {
return type;
}
//other methods
}
public enum ArmorType
{
SHIELD, BODY, HEAD, GLOVES, BOOTS, AMULET, RING, BELT, TRINKET;
}
Try the following:
public enum ArmorType
{
SHIELD(){
public ItemSlot getArmorSlot(Equipment equipment){
return equipment.offHand;
}
},
...
public abstract ItemSlot getArmorSlot(Equipment equipment);
}
Then call:
ItemSlot armorSlot = armor.getArmorType().getArmorSlot(equipment);
How about a HashMap in Equipment Class?
Like this:
public HashMap<String, ItemSlot> itemSlots = new ItemSlots HashMap<String, ItemSlot>();
Then in your constructor:
itemSlots.put("mainHand ", new ItemSlot());
You just then have to define a method like this:
public ItemSlot getItemSlot(String item) {
return itemSlots.get(item);
}
Finally, your case will be something like:
return equipment.getItemSlot(armor.getArmorType());
Yes, you can get away from the switch. Remember that enums are but statical guaranteed singletons. So they can have methods. Just do it like the following:
public enum ArmorType {
SHIELD {
public ItemSlot getItemSlot(Equipment e) { return e.offHand; }
},
// ... repeat for all other armor types
TRINKET {
public ItemSlot getItemSlot(Equipment e) { return e.special; }
};
public abstract ItemSlot getItemSlot(Equipment e);
}
Then you can simply call armorType.getItemSlot(equiment);.

Categories