 |
01.11.2005, 12:00
| Nach oben
#1 | | Projektleiter
Registriert seit: 30.11.2005 Ort: Bottrop
Beiträge: 1.160
| Speichern von Einstellungen - Welche API?
Aktuell stoße ich an die Grenzen, ab denen die Properties-API beginnt, extrem hässlich zu werden, deswegen suche ich nach nem geeigneten Ersatz.
Die Preferences-API wäre ein möglicher Zufluchtspunkt, verfügt aber auch nicht über alle notwendigen Funktionen.
Vielleicht sollte ich erstmal erklären, worum es geht. Folgendes also:
Ich möchte nicht nur einfache String-String-Paare speichern, sondern String-List<Object>.
z.B. möchte ich eine Liste von FTP-Daten speichern können, ohne mit hundertausend Keys rumspielen zu müssen (ich hab's aktuell mit der Properties-API realisiert und es ist ekelhaft).
Zur Verdeutlichung, hier mal ein Beispiel dessen, was ich mir vorstelle: PHP-Code: Registry registry = Registry.getInstance("ftpdata");
// oder Registry registry = Registry.getInstance(org.simpleedit.vfs.ftp.ConnectionData.class);
// Da eine Liste von "ConnectionData"-Beans gespeichert wurde, kann man diese gleich als Model verwenden
new JList(registry.getList("connectiondata"));
// schreiben der Liste:
List data = new ArrayList();
data.add(new ConnectionData("server", "user", "password"));
data.add(new ConnectionData("server", "user", "password"));
data.add(new ConnectionData("server", "user", "password"));
registry.set("connectiondata", data);
// Speichern der Registry
registry.store();
// Kind-Knoten ansprechen
Registry child = registry.getNode("somechild");
// Einfaches name-value-Paar ansprechen
System.out.println(child.get("name"));
Ein toller Zusatz wäre es, wenn die API auch noch XPath oder etwas ähnliches implementieren würde. PHP-Code: System.out.println(((ConnectionData)registry.xpath("/connectiondata[1]")).getUsername());
Mir wäre Commons-Configuration fast genug (auch wenn es nicht mit Serializable arbeitet), aber ich möchte mir nur ungern Commons-Collection (>500kb) und Commons-Lang (>130kb) mit aufhalsen müssen, für eine so kleine Aufgabe. Vorallem deshalb, weil ich die zusätzlichen Funktionen dieser Bibliotheken nicht verwenden würde.
Meine Frage wäre nun also, ob jemand von euch eine entsprechende API kennt oder selbst geschrieben hat und bereit wäre, sie mit dem Rest der Welt (bzw. für den Anfang mit mir) zu teilen. 
Weil aktuell hab ich nur begrenzte Lust, sowas selbst zu schreiben.
|
| |
02.11.2005, 15:43
| Nach oben
#2 | | Erfahrener Benutzer
Registriert seit: 28.08.2004 Ort: konstanz am bodensee
Beiträge: 190
|
hallo,
wenn es darum geht mehrdimensonale daten abzuspeichern sind properties sicherlich nicht das richtige format, entweder es ended wie du schon sagtest damit das man hunterttausend schlüssel für ein object/array hat oder man speichert die objekte/arrays als serialisierter werte, was aber dann unübersichtlich und umständlich wird wenn man von hand in der konfiguration rumhacken will...
ich benutze einen selbstgeschriebenen properties manager, der einfache arrays in einem wert speichert und das ganze auch noch für einen menschen zu lesen ist...
ein simples beispiel: Code: frame.background.color=[255,255,255]
frame.size=[600,400]
...
wenn man aber objekte speiechern will wird das umständlich weil man ja für jedes einzelne objekt ein mapping erstellen muss.
ich würde hier empfehlen die daten als xml datei zu speichern.
normalerweise müsste man ja auch hier ein spezielles mapping für jedes objekt erstellen, aber glücklicherweise gibt es ja genug kluge köpfe die sich um sowas gedanken machen...
so bin ich im letzen javamagazin auf XStream gestoßen, ein kleines framework (die grundausstattung ist 242kb sind zwar eigentlich auch viele nullen und einsen, aber immerhin ists mehr als die hälfte kleiner als die commons) mit dem man ohne mapping objekte und primitive datentypen serialisieren kann...
die verwendung ist sehr einfach, alle features werden durch eine Fassade angesprochen. Code: public class XTest {
static class ConnectionData{
String host;
String user;
String pass;
public ConnectionData(String host,String user,String pass){
this.host=host;
this.user=user;
this.pass=pass;
}
}
public static void main(String[] args) {
XStream x = new XStream();
x.alias("ConnectionData",ConnectionData.class);
// serialisieren
String xml = x.toXML(new ConnectionData("google.de","test","geheim"));
// desierialisieren
ConnectionData p = (ConnectionData)x.fromXML(xml);
}
}
der xml output für Person sieht dann so aus Code: <ConnectionData>
<host>google.de</host>
<user>test</user>
<pass>geheim</pass>
</ConnectionData>
du müsstest zwar die registry noch selbst programmieren, aber mit xstream währ der aufwand ziemlich gering...
mfg beny
|
| |
02.11.2005, 16:28
| Nach oben
#3 | | Projektleiter
Registriert seit: 30.11.2005 Ort: Bottrop
Beiträge: 1.160
| XStream sieht wirklich unglaublich gut aus! Das werde ich wohl nicht nur hier gebrauchen können.
Die ~250kb investiere ich gerne, weil ich dann direkt dom4j (~1MB) wieder rausschmeißen kann. 
Wirklich sehr nett. Mal schaun, was ich damit alles anstellen kann. Danke dir. |
| |
02.11.2005, 20:19
| Nach oben
#4 | | Erfahrener Benutzer
Registriert seit: 28.08.2004 Ort: konstanz am bodensee
Beiträge: 190
|
unkomplizierter gehts nicht.
hab echt nicht geglaubt das es so einfach ist, bis ichs mit eigenen augen gesehen hab...
kein stundenlanges rumgefummel mehr mit dom bäumen und auch kein dauerndes implementieren von SAXHandler für jeden scheiß...
wenn du eine schöne registry gebaut hast kannst du sie ja mal hier posten oder mir an beny_mcde ät gmx.de mailen,
ich mach im moment alle config geschichten mit strings und das wird mit der zeit nerfig weil man die werte immer umwandeln muss bevor man sie verwenden kann...
werd mir bald mal zeit nehmen um xstream ein bischen auszutesten...
was performance und so angeht, insbesonders wenn man große objektbäume(z.b. eine komplette benutzeroberfläche usw) serialisiert...
|
| |
04.11.2005, 20:25
| Nach oben
#5 | | Projektleiter
Registriert seit: 30.11.2005 Ort: Bottrop
Beiträge: 1.160
|
Soo... hab mir jetzt endlich was zusammengehackt. PHP-Code: /*
* Copyright 2005 Patrick Gotthardt
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.simpleedit;
import com.thoughtworks.xstream.XStream;
import java.util.*;
import java.io.*;
/**
* <p>This class is our powerful configuration-storage.</p>
* <p>In opposition to the common Java APIs like Properties and
* Preferences, this class is able to store every class you put into it.</p>
* <p>While it offers this really nice feature, its API is really simple and
* easy to use. Just use it like any other Map after you got an instance of it.</p>
* <p>You'll have to use the static {@link #getInstance(String id)}-method to get
* such an instance. Here is a simple example application:</p>
*
* <pre><code>public class RegistryTest {
public static void main(String[] args) {
// Obtain instance
Registry reg = Registry.getInstance("test");
// create a list and fill it
List testList = new ArrayList();
for(int i = 1; i <= 5; i++) {
testList.add("test "+i);
}
// store the list
reg.put("list", testList);
// store and close the registry
reg.store();
reg.close();
System.out.println("Registry created successfully");
System.out.println();
System.out.println("Loading registry, printing list:");
// Obtain a new instance
reg = Registry.getInstance("test");
testList = reg.getList("list");
for (int i = 0; i < testList.size(); i++) {
System.out.println(testList.get(i));
}
}
}</code></pre>
*
* <p>We won't waste any time explaining the obvious things now, instead we'll
* go to the more important parts.</p>
* <p>There's a small difference between {@link #store()} and {@link #save()}.
* The first one will call the second one, but hide its exception - if an exception
* was raised. Ok, it won't "hide" it, but it'll print its stack trace.</p>
* <p>If you're sure that no IOException will happen, then use {@link #store()}.</p>
*
* <p>The {@link #getInstance(String)}-method will use a cache so you won't get
* two or more instances of one registry object. Otherwise one part of your application
* might crash the changes of another part.</p>
* <p>To remove a Registry from the cache (for example if you only need it
* once in a long time and it'd make more sense to reload it when its required)
* you'll have to call the {@link #close()} method.</p>
* <p>Note that you won't be able to use the Registry-object after you
* called {@link #close()}. You'll get a lot of NullPointerExceptions if you use
* it after closing it, so just don't try.</p>
*
* <p>The registry won't store itself when it's closed. You'll have to do that manually.</p>
*
* <p>You might want to listen to changes of a specific registry (if you now some part of your application
* depends on this: don't close the registry as all listeners will be lost!).</p>
*
* @author Patrick Gotthardt
*/
public class Registry implements Map {
public static File configDirectory = new File(System.getProperty("user.home"), ".simpleedit/config");
// construction
private static XStream xstream;
private static Map<File, Registry> cache = new HashMap<File, Registry>();
private static XStream getXStream() {
if(xstream == null) {
xstream = new XStream();
}
return xstream;
}
public static Registry getInstance(String id) {
File configFile = new File(configDirectory, id+".xml");
Registry cachedRegistry = cache.get(configFile);
if(cachedRegistry != null) {
return cachedRegistry;
}
Registry reg = new Registry(configFile);
if(configFile.exists()) {
Reader config = null;
try {
config = new FileReader(configFile);
reg.delegate = (Map)getXStream().fromXML(config);
} catch (IOException e) {
e.printStackTrace();
} finally {
if(config != null) {
try {
config.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
} else {
reg.delegate = new HashMap();
}
cache.put(configFile, reg);
return reg;
}
// implementation details
private Map delegate;
private File configFile;
private List<RegistryListener> listeners;
protected Registry(File configFile) {
this.configFile = configFile;
listeners = new ArrayList<RegistryListener>();
}
public void store() {
try {
save();
} catch (IOException e) {
e.printStackTrace();
}
}
public void save() throws IOException {
Writer out = new FileWriter(configFile);
getXStream().toXML(delegate, out);
out.close();
}
public void close() {
cache.remove(configFile);
delegate = null;
configFile = null;
}
// event-stuff
public void addRegistryListener(RegistryListener l) {
listeners.add(l);
}
public void removeRegistryListener(RegistryListener l) {
listeners.remove(l);
}
protected void firePropertyChangedEvent(RegistryEvent e) {
for(int i = listeners.size()-1; i >= 0; i--) {
listeners.get(i).propertyChanged(e);
}
}
protected void firePropertyRemovedEvent(RegistryEvent e) {
for(int i = listeners.size()-1; i >= 0; i--) {
listeners.get(i).propertyRemoved(e);
}
}
// getXYZ-methods
public List getList(Object key) {
return (List)get(key);
}
public Map getMap(Object key) {
return (Map)get(key);
}
public int getInt(Object key) {
return ((Integer)get(key)).intValue();
}
public boolean is(Object key) {
return ((Boolean)get(key)).booleanValue();
}
public String getString(Object key) {
return (String)get(key);
}
// Simply delegate every Map-interface-method to delegate
public void clear() {
delegate.clear();
}
public boolean containsKey(Object key) {
return delegate.containsKey(key);
}
public boolean containsValue(Object value) {
return delegate.containsValue(value);
}
public Set entrySet() {
return delegate.entrySet();
}
public boolean equals(Object o) {
return delegate.equals(o);
}
public Object get(Object key) {
return delegate.get(key);
}
public int hashCode() {
return delegate.hashCode();
}
public boolean isEmpty() {
return delegate.isEmpty();
}
public Set keySet() {
return delegate.keySet();
}
public Object put(Object key, Object value) {
Object result = delegate.put(key, value);
firePropertyChangedEvent(new RegistryEvent(this, key));
return result;
}
public void putAll(Map t) {
delegate.putAll(t);
Set keys = t.keySet();
for(Object key : keys) {
firePropertyChangedEvent(new RegistryEvent(this, key));
}
}
public Object remove(Object key) {
firePropertyRemovedEvent(new RegistryEvent(this, key));
return delegate.remove(key);
}
public int size() {
return delegate.size();
}
public Collection values() {
return delegate.values();
}
}
PHP-Code: /*
* Copyright 2005 Patrick Gotthardt
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.simpleedit;
import java.util.EventObject;
/**
* @author Patrick Gotthardt
*/
public class RegistryEvent extends EventObject {
private Object key;
public RegistryEvent(Object source, Object key) {
super(source);
this.key = key;
}
public Object getKey() {
return key;
}
public Registry getRegistry() {
return (Registry)getSource();
}
}
PHP-Code: package org.simpleedit;
/**
* @author Patrick Gotthardt
*/
public interface RegistryListener {
public void propertyChanged(RegistryEvent event);
public void propertyRemoved(RegistryEvent event);
}
Wie deutlich zu erkennen ist, fehlt noch ein Großteil der Dokumentation, aber ich denke, dass das ganze so akzeptabel ist.
Kommentare, Vorschläge, etc. wären mir lieb. 
(Ich hab da jetzt nicht allzu lang drüber nachgedacht.)
|
| | |
Aktive Benutzer in diesem Thema: 1 (Registrierte Benutzer: 0, Gäste: 1) | | | | Themen-Optionen | Thema durchsuchen | | | |
Forumregeln
| Es ist dir nicht erlaubt, neue Themen zu verfassen. Es ist dir nicht erlaubt, auf Beiträge zu antworten. Es ist dir nicht erlaubt, Anhänge hochzuladen. Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten. HTML-Code ist aus. | | | Alle Zeitangaben in WEZ +1. Es ist jetzt 21:25 Uhr.
|