So my problem is, that I have an serilized ArrayList and have to update it in my GUI to show its content in a ListView dynamically.
The serialization and deserialization works fine with the use of a DAO interface but the GUI won't refresh my ListView.
This class holds my data interaction (mostly save, load...):
public class Medienverwaltung implements Serializable, IDAO{
private static final long serialVersionUID = 1L;
private List<Medium> medienliste;
public ObservableList<Medium> obList; //public for test-reasons
public Medienverwaltung(){
medienliste = new ArrayList<Medium>();
obList = FXCollections.observableArrayList(medienliste);
}
//[...]
public List<Medium> getMedienliste(){
return this.medienliste;
}
//[...]
}
Here comes my GUI implementation snippet:
public class HauptFenster extends Application{
private Medienverwaltung medienverwaltung;
#Override
public void start(Stage stage) throws Exception{
medienverwaltung = new Medienverwaltung();
VBox root = new VBox();
ListView<String> showliste = new ListView<String>();
MenuBar menuBar = createMenuBar(stage);
root.getChildren().add(menuBar);
root.getChildren().add(showliste);
//Make Listener and refresh the shown list!
medienverwaltung.obList.addListener(new ListChangeListener<Medium>(){
#Override
public void onChanged(ListChangeListener.Change<? extends Medium> change) {
showliste.getItems().clear();
for(Medium medium : medienverwaltung.obList){
//toString() is overwritten and works, too
showliste.getItems().add(medium.toString());
}
}
});
// this adds a Medium object to the Arraylist in Medienverwaltung
medienverwaltung.aufnehmen(new Bild("Foto12", 2017, "Zuhause"));
stage.setTitle("Medien Verwaltung");
stage.setScene(new Scene(root, 800, 400) );
stage.show();
}
//[...]
I also tired to exchange the whole ArrayList from the class "Medienverwaltung" with an ObservableList, so that there is only one List remaining, which works for the GUI but not for the serialization and deserialization as I guessed before. (and tried a few other implementations)
Does anyone have an idea how to change my code so that it works?
And my second question is, what is the best way in terms of a 3 layer architecture?
The following is a reference to Fabians Answer and responds to my comment on that
Update#1.1 (addendum for explanation)
public interface IDAO {
// Save method
void speichern(List<Medium> liste) throws PersistenzException;
// Load method
List<Medium> laden() throws PersistenzException;
}
Here comes my concrete save Method:
#Override
public void speichern(List<Medium> medienliste) throws PersistenzException{
File sfile = new File("medienliste.dat");
try(FileOutputStream fos = new FileOutputStream(sfile); ObjectOutputStream oos = new ObjectOutputStream(fos)){
oos.writeObject(medienliste);
System.out.println("Serialisierung erfolgreich!");
}catch(IOException e){
e.printStackTrace();
System.out.println("Serialisierung fehlgeschlagen!");
}
}
Update#1.2 (addendum for explanation)
//[...] section of my GUI for saving
MenuItem speichern = new MenuItem("Speichern");
speichern.setOnAction(new EventHandler<ActionEvent>(){
#Override
public void handle(ActionEvent e){
try{
//Before: medienverwaltung.speichern(medienverwaltung.getMedienliste()); -> doesn't work because of serializing an ObservableList
medienverwaltung.speichern(medienverwaltung.getBackingList());
}catch(PersistenzException pe){
pe.printStackTrace();
}
}
});
//[...]
But as I guess, it's not a fine way to access the backinlist this way.
Update#2:
to respect the principle of encapsulation in a clean way I now added an overloaded Method in the class Medienverwaltung:
public void speichern() throws PersistenzException{
speichern(backingList);
}
So my GUI now only calls speichern(). This actually calls the method for saving with the backedlist which is no more accessible from the outside. I hope this is no bad coding style ^^
BTW.: If you are reading this and have a similar problem, don't use ObservableArrayList for the synchronisation with a normal List, this won't work! Use ObservableList instead.
Hide the backing list (medienliste) from other classes by removing the getter. If you modify this list using the ObservableList the ListView (or every other object that has added a listener to the list) will properly update.
Furthermore unless Medium extends Node you can simply use this kind of object as items of the ListView, since the cells set the text to the result of the toString method called for the associated item by default.
public class Medienverwaltung implements Serializable, IDAO{
private static final long serialVersionUID = 1L;
private List<Medium> backingList;
// transient field not persisted
private transient ObservableList<Medium> medienliste;
public Medienverwaltung(){
backingList = new ArrayList<Medium>();
medienliste = FXCollections.observableArrayList(backingList);
}
// make sure an ObservableList is created when reading the serialized object
private void readObject(ObjectInputStream inputStream) throws IOException, ClassNotFoundException {
inputStream.defaultReadObject();
medienliste = FXCollections.observableArrayList(backingList);
}
//[...]
public ObservableList<Medium> getMedienliste(){
return this.medienliste;
}
//[...]
}
#Override
public void start(Stage stage) throws Exception{
medienverwaltung = new Medienverwaltung();
VBox root = new VBox();
ListView<Medium> showliste = new ListView<>(medienverwaltung.getMedienliste());
MenuBar menuBar = createMenuBar(stage);
root.getChildren().add(menuBar);
root.getChildren().add(showliste);
// this adds a Medium object to the Arraylist in Medienverwaltung
medienverwaltung.aufnehmen(new Bild("Foto12", 2017, "Zuhause"));
stage.setTitle("Medien Verwaltung");
stage.setScene(new Scene(root, 800, 400) );
stage.show();
}
Note that the Medienverwaltung.aufnehmen method should not work directly with the backing list - it should use the ObservableList instead to make sure changes can be observed...
EDIT
Looking at the IDAO interface it should probably be a object different to Medienverwaltung, since otherwise you'd violate the seperation of concerns design principle; also it wouldn't make sense to pass a value as parameter that's already contained as property of the object itself.
It seems that the IDAO object should be responsible for reading/writing the list data only which would make implementing Serializable with Medienverwaltung unnecessary. Probably something like this is expected solution to your excercise:
IDAO idao = new IDAOImplementation();
Medienverwaltung medienverwaltung = new Medienverwaltung(idao.laden());
public void handle(ActionEvent e){
try{
idao.speichern(medienverwaltung.getMedienliste());
}catch(PersistenzException pe){
pe.printStackTrace();
}
}
public Medienverwaltung(List<Medium> medien) {
this.medienliste = FXCollections.observableArrayList(medien);
}
The IDAO implementation should most likely not depend on the implementation of the List and therefore not expect the List to be serializable. You can simply work around non-serialized lists by a) not using ObjectOutputStream to persist the data, but some other way not relying on serializable objects or b) simply copy the contents of the list to a serializable list:
#Override
public void speichern(List<Medium> medienliste) throws PersistenzException{
File sfile = new File("medienliste.dat");
try(FileOutputStream fos = new FileOutputStream(sfile); ObjectOutputStream oos = new ObjectOutputStream(fos)){
oos.writeObject(new ArrayList(medienliste));
System.out.println("Serialisierung erfolgreich!");
} catch(IOException e){
throw new PersistenzException(e);
}
}
Related
In my Plugin there is an action to open an Editor (extends EditorPart). When I try to open it a second time, its init method isn't called. Instead the focus is shifted to the editor that is already open.
The Editor is associated with a filetype. Here is the excerpt from the plugin.xml:
<extension point="org.eclipse.ui.editors">
<editor
class="de.blub.tool.ide.editors.GRASPEditor"
default="true"
extensions="grasp"
filenames="*.grasp"
icon="icons/newGraspFile.png"
id="de.blub.tool.ide.editors.GRASPEditor"
name="GRASP File Editor">
</editor>
</extension>
I have an Action to open a new Editor. When I try to click that Action twice it reuses the first Editor. I also tried to use an EditorMatcher that implements IEditorMatchingStrategy and always returns false in its matches() method. Even that doesn't change the behavior.
This seems to be a desired/default behavior in eclipse. How can I change that so that the user can initialize a new Editor each time?
Eclipse looks for the equals method of the IEditorInput instance. The Editor somewhere in its code (in my case in the doSave method) uses a setInput method like this:
#Override
public void init(IEditorSite site, IEditorInput input) throws PartInitException {
// Initialize the editor input
this.input = new MyInputClass(resource);
...
}
#Override
public void doSave(IProgressMonitor monitor) {
...
setInput(input);
}
MyInputClass is the class that extends IEditorInput. The logic for eclipse to reuse an Editor or create a new one is in its equals method. The following example checks the path of an IResource field:
public class MyInputClass implements IEditorInput {
private IResource resource;
public MyInputClass(IResource resource) {
this.resource = resource;
}
public IResource getResource() {
return resource;
}
public void setResource(IResource resource) {
this.resource = resource;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj instanceof MyEditorClass) {
MyEditorClass other = (MyEditorClass) obj;
if (getResource().getFullPath().equals(other.getResource().getFullPath())) {
return true;
}
}
return false;
}
}
Of course one can define another logic inside the equals method. Make sure to not create a chaos, which is very well possible, as greg-449 pointed out in a comment.
As far as I can tell, the Expando class in Kephas allows adding new members on the fly. Unlike the ExpandoObject in .NET, I noticed it is not sealed, so I could change its behavior, but I don't really know how.
[EDITED]
My scenario is to make the expando readonly at a certain time.
Try this snippet:
public class ReadOnlyExpando : Expando
{
private bool isReadOnly;
public ReadOnlyExpando()
{
}
public ReadOnlyExpando(IDictionary<string, object> dictionary)
: base(dictionary)
{
}
public void MakeReadOnly()
{
this.isReadOnly = true;
}
protected override bool TrySetValue(string key, object value)
{
if (this.isReadOnly)
{
throw new InvalidOperationException("This object is read only").
}
return base.TrySetValue(key, value);
}
}
For other scenarios you may want to check the LazyExpando class, which provides a way to resolve dynamic values based on a function, also handling circular references exception.
It might be really trivial but I am having some trouble with some variables in C# console app(Rewriting from Winform app). Here is a bit of code so you can get the problem.
Public partial class Form1:Form
{
private string ArchieveDir;
private string IncomingDir;
private string ProblemDir;
private string DuplicateDir;
private string Downloader App;
public Form1()
{
InitializeComponent();
System.Data.SqlClient.SqlConnection myCon = new System.Data.SqlClient.SqlConnection(X.Settings.Settings.ConnectionString);
XmlDocument cfg = new XmlDocument();
try
{
cfg.Load("config.xml");
ArchiveDir = cfg.SelectSingleNode("/ExcelRecalc/Archive").InnerText;
IncomingDir = cfg.SelectSingleNode("/ExcelRecalc/Incoming").InnerText;
ProblemDir = cfg.SelectSingleNode("/ExcelRecalc/Problem").InnerText;
DuplicateDir = cfg.SelectSingleNode("/ExcelRecalc/Duplicate").InnerText;
DownloaderApp = cfg.SelectSingleNode("/ExcelRecalc/DownloaderApp").InnerText;
}
catch( Exception ex)
{
System.Console.WriteLine(ex.Message);
}
}
[STAThread]
static void Main()
{
Application.Run(new Form1());
}
private bool Calculate(DataRow dr)
{
//Application quite long, used in method DownloadIntoDir//
}
private void DownloadIntoDir()
{
//Require no user input, get files from specified directory, uses Calculte to perform calculation, saves into DB using a conn//
}
private void ConnectRecords()
{
//Requires no user input, connects file (produced by DownloadIntoDir) to another table in DB//
}
private void Form1_Load(object sender, EventArgs e)
{
SqlConnection conn = new SqlConnection(GSI.Settings.Settings.ConnectionString);
DownloadIntoDir();
ConnectRecords();
Application.Exit();
}
}
}
Okay that is pretty much the whole program. Methods Calculate, DownloadIntoDir and ConnectRecords are all working fine. However, in Winform, I have saved DB connection and variables, which store directory info into app form, which then could be accessed by all methods. (Both ,directory and connection, are required for all methods). It functions perferctly fine ( and without user input) in Winform. However I have trouble with storing variables and connection in such a way that it is available to all methods. What would be the best approach?
I recommend defining a class that contains properties for all of the variables you need to access throughout your program, then have a single instance of that class as a private readonly variable, and initialize it in your constructor.
The one thing to make sure of is that you're keeping everything safe if you're using threads. If you are using threads, I recommend checking out this page.
I have a class that only have main which read in some txt and do the algorithms.
my class is look like:
class doThejob{
public static void main(String args[]){
//*****start part A******
//do the reading from text file, and tokenize it
// process into the form I need,
//about 10-30 lines of codes
//******End of part A*****
//then run the algorithms
algorithm alg=new aglorithm();
Object output = alg.x(input);
//****Part B**** output to txt, about 10~40 lines
}
}
class algorithm{
private void a(Object x){
//do something
return (Object)result;
}
}
Can anyone tell me should I extract those part A and part B to a new class ,and then setup them as a public method .like below
class Io{
public Object readFromTxt(String path){
}
public void outputToTxt(String path){
}
}
And if I setup them , and then use it like below, is that more OOP?
class doThejob{
public static void main(String args[]){
Io dataProcess= new Io();
Object input = dataProcess.readFromTxt(args[0]);
algorithm alg=new aglorithm();
Object output =alg.x(input);
dataProcess.readFromTxt(args[1],output);
}
}
class algorithm{
private Object a(Object x){
//do something
}
}
Do it the way you fill is more readable.
Separating this in another class is according to the Single Responsability Principle. It will help making the code more readable and easy to change later on.
If you want to expand more on this, you could create an interface (eg.: IIO) for input and output. This way you can implement this interface in the IO class, renaming it to FileIO. Anytime you want to create another form of IO, like database access, you just have to create a DatabaseIO class that implements this interface and change the instance in the main method for this new type:
public interface IIO
{
string Read();
void Write(string text);
}
public class FileIO : IIO
{
string path;
public FileIO(string filePath)
{
path = filePath;
}
public string Read()
{
// read from file and return contents
}
public void Write(string text)
{
// write to file
}
}
public class SqlServerIO : IIO
{
SqlConnection conn;
public SqlServerIO(string connectionStringName)
{
// create the connection
}
public string Read()
{
// read from database
}
public void Write(string text)
{
// write to database
}
}
Extracting interfaces makes the code more maintenable by alowing to switch implementations anytime without messing with working code. It also facilitates unit testing.
I'm personally committed to .net distributed caching solutions, but I think this question is interesting across all platforms.
Is there a distributed caching solution (or generic strategy) that allows to both store objects in the cache while maintaining the integrity of the references between them?
To exemplify - Suppose I have an object Foo foo that references an object Bar bar and also and object Foo foo2 that references that same Bar bar. If I load foo to the cache, a copy of bar is stored along with it. If I also load foo2 to the cache, a separate copy of bar is stored along with that. If I change foo.bar in the cache, the change does not impact foo2.bar :(
Is there an existing distributed cache solution that will enable me to load foo, foo2 and bar into the cache while maintaining the foo.bar foo2.bar references?
First and foremost
I do not know of any distributed system, and I do not pretend to build one. This post explains how you can simulate this behavior with .NET and C# using the IObjectReference interface with serializable objects.
Now, lets go on with the show
I do not know of such a distributed system, but you can somewhat easily achive this with .NET using the IObjectReference interface. Your implementation of ISerializable.GetObjectData would need to call SerializationInfo.SetType to point out a proxy class that implements IObjectReference, and would be able (with help from data provided by your GetObjectData method) to get a reference to the real object that should be used.
Example code:
[Serializable]
internal sealed class SerializationProxy<TOwner, TKey> : ISerializable, IObjectReference {
private const string KeyName = "Key";
private const string InstantiatorName = "Instantiator";
private static readonly Type thisType = typeof(SerializationProxy<TOwner, TKey>);
private static readonly Type keyType = typeof(TKey);
private static readonly Type instantiatorType = typeof(Func<TKey, TOwner>);
private readonly Func<TKey, TOwner> _instantiator;
private readonly TKey _key;
private SerializationProxy() {
}
private SerializationProxy(SerializationInfo info, StreamingContext context) {
if (info == null) throw new ArgumentNullException("info");
_key = (TKey)info.GetValue(KeyName, keyType);
_instantiator = (Func<TKey, TOwner>)info.GetValue(InstantiatorName, instantiatorType);
}
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) {
throw new NotSupportedException("This type should never be serialized.");
}
object IObjectReference.GetRealObject(StreamingContext context) {
return _instantiator(_key);
}
internal static void PrepareSerialization(SerializationInfo info, TKey key, Func<TKey, TOwner> instantiator) {
if (info == null) throw new ArgumentNullException("info");
if (instantiator == null) throw new ArgumentNullException("instantiator");
info.SetType(thisType);
info.AddValue(KeyName, key, keyType);
info.AddValue(InstantiatorName, instantiator, instantiatorType);
}
}
This code would be called with SerializationProxy.PrepareSerialization(info, myKey, myKey => LoadedInstances.GetById(myKey)) from your GetObjectData method, and your LoadedInstances.GetById should return the instance from a Dictionary<TKey, WeakReference> or load it from cache/database if it isnt already loaded.
EDIT:
I've wrote some example code to show what I mean.
public static class Program {
public static void Main() {
// Create an item and serialize it.
// Pretend that the bytes are stored in some magical
// domain where everyone lives happily ever after.
var item = new Item { Name = "Bleh" };
var bytes = Serialize(item);
{
// Deserialize those bytes back into the cruel world.
var loadedItem1 = Deserialize<Item>(bytes);
var loadedItem2 = Deserialize<Item>(bytes);
// This should work since we've deserialized identical
// data twice.
Debug.Assert(loadedItem1.Id == loadedItem2.Id);
Debug.Assert(loadedItem1.Name == loadedItem2.Name);
// Notice that both variables refer to the same object.
Debug.Assert(ReferenceEquals(loadedItem1, loadedItem2));
loadedItem1.Name = "Bluh";
Debug.Assert(loadedItem1.Name == loadedItem2.Name);
}
{
// Deserialize those bytes back into the cruel world. (Once again.)
var loadedItem1 = Deserialize<Item>(bytes);
// Notice that we got the same item that we messed
// around with earlier.
Debug.Assert(loadedItem1.Name == "Bluh");
// Once again, force the peaceful object to hide its
// identity, and take on a fake name.
loadedItem1.Name = "Blargh";
var loadedItem2 = Deserialize<Item>(bytes);
Debug.Assert(loadedItem1.Name == loadedItem2.Name);
}
}
#region Serialization helpers
private static readonly IFormatter _formatter
= new BinaryFormatter();
public static byte[] Serialize(ISerializable item) {
using (var stream = new MemoryStream()) {
_formatter.Serialize(stream, item);
return stream.ToArray();
}
}
public static T Deserialize<T>(Byte[] bytes) {
using (var stream = new MemoryStream(bytes)) {
return (T)_formatter.Deserialize(stream);
}
}
#endregion
}
// Supercalifragilisticexpialidocious interface.
public interface IDomainObject {
Guid Id { get; }
}
// Holds all loaded instances using weak references, allowing
// the almighty garbage collector to grab our stuff at any time.
// I have no real data to lend on here, but I _presume_ that this
// wont be to overly evil since we use weak references.
public static class LoadedInstances<T>
where T : class, IDomainObject {
private static readonly Dictionary<Guid, WeakReference> _items
= new Dictionary<Guid, WeakReference>();
public static void Set(T item) {
var itemId = item.Id;
if (_items.ContainsKey(itemId))
_items.Remove(itemId);
_items.Add(itemId, new WeakReference(item));
}
public static T Get(Guid id) {
if (_items.ContainsKey(id)) {
var itemRef = _items[id];
return (T)itemRef.Target;
}
return null;
}
}
[DebuggerDisplay("{Id} {Name}")]
[Serializable]
public class Item : IDomainObject, ISerializable {
public Guid Id { get; private set; }
public String Name { get; set; }
// This constructor can be avoided if you have a
// static Create method that creates and saves new items.
public Item() {
Id = Guid.NewGuid();
LoadedInstances<Item>.Set(this);
}
#region ISerializable Members
public void GetObjectData(SerializationInfo info, StreamingContext context) {
// We're calling SerializationProxy to call GetById(this.Id)
// when we should be deserialized. Notice that we have no
// deserialization constructor. Fxcop will hate us for that.
SerializationProxy<Item, Guid>.PrepareSerialization(info, Id, GetById);
}
#endregion
public static Item GetById(Guid id) {
var alreadyLoaded = LoadedInstances<Item>.Get(id);
if (alreadyLoaded != null)
return alreadyLoaded;
// TODO: Load from storage container (database, cache).
// TODO: The item we load should be passed to LoadedInstances<Item>.Set
return null;
}
}