Object ID externalization
Hello,
I might missed it but I did not find any support for Object ID
externalization in JDO specs.
I believe it is a much needed feature to be able to externalize Object
ID to lets say char array and being able to re-create Object ID from its
external form.
One simple example - passing Object ID as parameter to HTTP URL - you
need to externalize it first to produce URL and inside servlet you would
need to recreate the Object ID from its external form
Alex Roytman
Peace Technology, Inc.
301-206-9696 ext. 103
Hi Alex,
We are looking at a few methods that will help this situation, and will
probably adopt them in the next version of the specification.
The user-visible one is in PersistenceManager:
Object newObjectIdInstance(Class pcClass, String stringifiedObjectId).
The stringifiedObjectId is the result of toString on an object id:
stringifiedObjectId =
PersistenceManager.getObjectId(pcObject).toString().
Does this work for you?
Craig
"Roytman, Alex" wrote:
>
Hello,
I might missed it but I did not find any support for Object ID
externalization in JDO specs.
I believe it is a much needed feature to be able to externalize Object
ID to lets say char array and being able to re-create Object ID from its
external form.
One simple example - passing Object ID as parameter to HTTP URL - you
need to externalize it first to produce URL and inside servlet you would
need to recreate the Object ID from its external form
Alex Roytman
Peace Technology, Inc.
301-206-9696 ext. 103--
Craig Russell 650 786-7819
Architect mailto:[email protected]
Sun Microsystems, Inc. http://access1.sun.com/jdo
Similar Messages
-
RE: Object ID externalization
Oh, sorry I missed it
-----Original Message-----
From: David Ezzio [mailto:[email protected]]
Sent: Monday, August 13, 2001 9:25 PM
To: [email protected]
Cc: JDO-ListServ
Subject: Re: Object ID externalization
Alex,
I think the new method Craig proposed
PersistenceManager.newObjectIdInstance(...) is the API that you want to
go from stringifiedObjectId to an ObjectId. It is possible however that
we may have to post and pre process the stringifiedObjectId to make it
happy with various protocols that we might want to pass it through.
DavidAlex,
I think the new method Craig proposed
PersistenceManager.newObjectIdInstance(...) is the API that you want to
go from stringifiedObjectId to an ObjectId. It is possible however that
we may have to post and pre process the stringifiedObjectId to make it
happy with various protocols that we might want to pass it through.
David -
Stupid NPE, passing over RMI, it is externalizable.
Hi,
I have an object (ObjectA) which holds a 2 element array of objects (ObjectB). Both objects are externalizable and have the read/writeExternal methods implemented. However when the server side receives the object the array of objects (ObjectB) contains all null values. Anyone any ideas?
ThanksThe null pointer exception occurs when I try to access a string in ObjectB. Its actually a 4 element array also. (Dont ask!).
Code:
//This is in the writeExternal
if (objectBInstance!= null && objectBInstance.length > 0) {
oo.writeInt(objectBInstance.length);
oo.writeInt(objectBInstance[0].length);
oo.writeInt(objectBInstance[0][0].length);
oo.writeInt(objectBInstance[0][0][0].length);
for (int i = 0; i < objectBInstance.length; i++) {
for (int j = 0; j < objectBInstance.length; j++) {
for (int k = 0; k < objectBInstance[i][j].length; k++) {
for (int l = 0; l < objectBInstance[i][j][k].length; l++) {
oo.writeObject(objectBInstance[i][j][k][l]);
} else {
oo.writeInt(0);
//This is in the readExternal
length = oi.readInt();
if (length > 0) {
int length2 = oi.readInt();
int length3 = oi.readInt();
int length4 = oi.readInt();
objectBInstance= new ObjectB[length][length2][length3][length4];
for (int i = 0; i < length; i++) {
for (int j = 0; j < length2; j++) {
for (int k = 0; k < length3; k++) {
for (int l = 0; l < length4; l++) {
objectBInstance[i][j][k][l] = new ObjectB();
objectBInstance[i][j][k][l] = (ObjectB)oi.readObject();
Messy i know!
The read/writeExternal in the objects used in ObjectB are ok too. -
Why JMS if we can use JAVA classes for the same
if we can communicate using the java classes which are protable and platform independent why do we have to use JMS for that. what is so specific about it and what is the benefit that makes it outstanding
James/Steve,
I feel that some of your points are misleading. The original authors point was a comparison against JMS and other means of achieving similar functionality with Java. Regarding your combined points:
2) Scalability is something that can be achieved regardless of whether JMS is used. Clustering has nothing to do with JMS, each vendors clustering implementation is entirely proprietary.
4) Aside from the inclusion of JNDI the JMS specification offers nothing for management. You only need to look through this forum at the amount of times a question like �how do I make a topic programmatically?� has been asked. All good JMS vendors offer management API�s and tools but all are vendor specific implementations.
10) There is no JMS standard for load balancing. The generic round robin features of a JMS queue falls a long way short of true �load balancing�. Any vendor (ourselves included) that offers load balancing does so in an entirely proprietary manner.
11) Again�JMS provides no standards for clustering of running one or more server. This is entirely down to implementation and functionality is vendor specific.
12) Dead Message/Letter Queues are not defined in JMS
14) What does serialization have to do with the original point? How one chooses to serialize their content ( XML, Object Serialization, Externalizable etc..) is equally applicable to both bespoke Java coding and JMS implementations.
To birs1982:
JMS is merely a collection of interfaces that define how one might read and write to a topic or queue. Vendors (both open source and commercial) recognize that these interfaces alone are not enough to offer an entire middleware messaging solution and complete the offering with sophisticated management, resilience, clustering, security and other value added features. These value added features require considerable effort to get right as do things like scalability and performance.
Given JMS like messaging is almost commodity now (just look at the amount of JMS vendors that have leaped into the ESB space) and a common (perhaps essential) infrastructure component why produce it yourself? Surely your time is better spent serving your clients and business lines rather that writing what amounts to plumbing. All the hard work has been done and the price for this hard work (depending on your requirements) is anything from free to many thousands of pounds.
Regards,
Paul Brant
my-Channels - Technologies working together
http://www.my-channels.com/ -
How ObjectInputStream.readObject() works??
Below is Test.java, some codes on simple serialization operations (copied from a book.) A "Blip1" object is created, serialized to a file, then deserialized back from the file. Everything works fine!!
My question is, why does the last line work, "b1 = (Blip1)in.readObject();" ?
I mean, ObjectInputStream is part of [package java.io], how can we create a "Blip1" object of [package anypackage] while we are inside [package java.io]?? Am I missing something fundamental?
I thought this ObjectInputStream.readObject() is based on Reflection, that is, ObjectInputStream.readObject() created the "Blip1" object based on the class name. So I wrote Creator.java (see below) and the result was an IllegalAccessException --- we cannot create a "Blip1" object while we are inside another package. This shows that ObjectInputStream.readObject() is not really based on Reflection(??)
How does ObjectInputStream.readObject() work??
//Test.java
package anypackage;
class Blip1 implements Externalizable {
public Blip1() {System.out.println("Blip1 created");}
public void writeExternal(ObjectOutput out) throws IOException {}
public void readExternal(ObjectInput in)
throws IOException, ClassNotFoundException {}
public class Test {
public static void main(String[] args)
throws IOException, ClassNotFoundException {
System.out.println("Constructing objects:");
Blip1 b1 = new Blip1();
ObjectOutputStream o = new ObjectOutputStream(
new FileOutputStream("Blips.out"));
System.out.println("Saving objects:");
o.writeObject(b1);
o.close();
// Now get them back:
ObjectInputStream in =
new ObjectInputStream(
new FileInputStream("Blips.out"));
System.out.println("Recovering b1:");
b1 = (Blip1)in.readObject();
//Creator.java
package anyotherpackage;
import java.util.*;
public class Creator{
Object o;
public Creator(){
try{
Class c = Class.forName("anypackage.Blip1");
Object o = c.newInstance();
} catch (Exception e){
System.out.println(e);
public static void main(String[] args){
Creator c = new Creator();
}As far as i understand the ObjectInputStream.readObject() create object by recursively building the memory owned by the object, obviously object Ctor is not called (otherwise serializeable classes were forced to have empty Ctor).
It goes like this -
1)when object implements Serializable interface its stream hold description of the the object memory (for each datamember we will have something like offset,datamember type, and streamed datamember), ObjectInputStream.readObject build the datamembers recursively (recursion is stopped when we have primitive type datamember)
2)when object implements Externalizable interface the programmer is responsible to stream object's datamembers to the ObjectOutput and to read them from the ObjectInput, ObjectInputStream is responsible to create an empty object for you
anyway no Ctors are involved -
Does CohQL backup support POF?
I'm trying to use the CohQL command line application to backup my test data set and I'm getting the following (with the actual package and class name changed):
java.io.NotSerializableException: mypackage.MyClass$Key
java.lang.RuntimeException: Error in BACKUP
The class I'm caching instances of, including the inner class used as a key, all implement PortableObject, but not Serializable. I've changed the query.sh script to include the same tangosol.coherence.cacheconfig, tangosol.pof.config, etc. parameters than I'm using in the other processes that are populating and reading from the cache, and I can query the cache using CohQL w/o any problems. Should this work or does CohQL just not support backing up POF objects?
Thanks.It's because backup option doesn't support POF objects.
I've did a quick lookup in the import references within CohQL classes, from a first look it seems like backup is supported only for cache objects, implementing Externalizable or ExternalizableLite interface.
Yet another bug/unfinished feature in 3.6 version, it's especially annoying since Coherence docs are encouraging use of POF literally on every page.
Taking into account this ant other bugs within key & new functionality sets I assume the quality of code & testing has dropped significantly in release 3.6.
And I have really bad guesses on reasons of that. :( -
Writing Objects to file using Externalizable
Hi,
I'm trying to write an object to file. My sample code is:
public class Junk implements Externalizable{
private static java.util.Random generator = new java.util.Random();
private int answer;
private double[] numbers;
private String thought;
public Junk(String thought) {
this.thought = thought;
answer = 42;
numbers = new double[3+ generator.nextInt(4)];
for (int i=0; i<numbers.length; i++) {
numbers[i] = generator.nextDouble();
public void writeExternal(ObjectOutput stream) throws java.io.IOException {
stream.writeInt(answer);
stream.writeBytes(thought);
for(int i=0; i< numbers.length; i++) {
stream.writeDouble(numbers);
public void readExternal(ObjectInput stream) throws java.io.IOException {
answer = stream.readInt();
String thought = stream.readUTF();
and the class with main() is:
package MyTest;
import java.io.*;
public class SerializeObjects {
public SerializeObjects() {
public static void main(String args[]) {
Junk obj1 = new Junk("A green twig is easily bent.");
Junk obj2 = new Junk("A little knowledge is a dangerous thing.");
Junk obj3 = new Junk("Flies light on lean horses.");
ObjectOutputStream oOut = null;
FileOutputStream fOut = null;
try {
fOut = new FileOutputStream("E:\\FileTest\\test.bin");
oOut = new ObjectOutputStream(fOut);
obj1.writeExternal(oOut);
//obj2.writeExternal(oOut);
} catch (IOException e) {
e.printStackTrace(System.err);
System.exit(1);
try {
oOut.flush();
oOut.close();
fOut.close();
} catch(IOException e) {
e.printStackTrace(System.err);
System.exit(1);
The output I get in test.bin contains some junk ascii codes. The only item that is written correctly in the file is the string.
Is there anyway I can write correct data into a file?
My output needs to be a readable text format file.
Can anyone help please?obj1.writeExternal(oOut);This should be
oOut.writeObject(obj1);However,
The output I get in test.bin contains some junk ascii
codes. The only item that is written correctly in the
file is the string.If you don't want 'junk' don't use Externalizable and ObjectOutputStream at all, just use PrintStream/PrintWriter.println(). -
Externalizable and compatibility with existing serialized objects
I have an object in a db in a serialized state as a result of writeExternal. I want to change the class's writeExternal and readExternal such that a String that should have been serialized is now serialized.
What is the best practice for supporting existing serialized versions of the class being read by my amended version?
Should I add to the end of readExternal and writeExternal and catch an IOException in readExternal? Will readExternal even through an IOException if my stream is exhausted? And I guess IOException could be caught for other reasons that stream exhaustion so I should not rely on it being thrown for that reason alone?
writeExternal(ObjectOutput out)
out.writeObject(m_1);
out.writeObject(m_2);
out.writeObject(m_theNewOne);
readExternla(ObjectInput in)
m_1 = (String)in.readObject();
m_2 = (String)in.readObject();
try
m_theNewOne = (String)in.readObject();
catch (IOException e)
m_theNewOne = "default value";
Thanks.If your readExternal method tries to data that isn't there it will get a -1 from primitive reads, EOFException from DataInputStream.readXXX methods, or from readObject() calls it will get an OptionalDataException with the eof field set to true.
See http://java.sun.com/j2se/1.5.0/docs/guide/serialization/spec/input.html#6014. -
Read data from Vector object serialized in a file issue!!
Hi all,
I have two classes :CBase and CHelper class in a package and serializing some information in a file and then reading it. I am facing some problem while reading the data
which was stored using vector of objects. Below is the detailed problem:::::
////////////CHELPER
public class CHelper implements Externalizable, Cloneable {
public string szname;
//..other functions writeexternal read external
////////////CBASE
public class CBase
implements Externalizable, Cloneable {
protected boolean bRead;
protected boolean bWrite;
protected Vector vData;
public void addInstance(CHelper obj) {
vData.addElement(obj);
//..other functions writeexternal read external
I am Serializing all the CBase information in a file and trying to read the contents from this serialized file::::
CBase base = new CBase();
CHelper obj = new CHelper();
obj.setName("HELPER");
base.addInstance(obj); // ADDING THE OBJECT DATA INTO THE VECTOR
base.bRead = true;
base.bWrite = true;
// Writing to the file
FileOutputStream fs = new FileOutputStream("d:\\temp\\Base.txt");
ObjectOutputStream os = new ObjectOutputStream(fs);
os.writeObject(base);
os.close();
//reading from the file
FileInputStream fi = new FileInputStream("d:\\temp\\Base.txt");
ObjectInputStream oi = new ObjectInputStream(fi);
CBase read = (CBase) oi.readObject();
PRINTING THE OUTPUT
System.out.println("Vector = " + read.vData.firstElement().toString());
System.out.println("Write = " + read.bRead);
System.out.println("Read = " + read.bWrite);
The output is something like:
Vector = com.sp.CHelper@14b7453 // WHAT IS THIS ?? GARBAGE ?? NOT ABLE TO ANALYSE :(
Write = true;
Read = true;************
MY PROBLEM:
i.e How to get the information stored in the Vector ? I am getting something "com.sp.CHelper@14b7453 " whereas the expected output should be Vector = HELPER. I am
writing and reading the vector as follows:
// CHELPER
public void writeExternal(ObjectOutput out) throws IOException {
out.writeUTF(szName.toString());
public void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException {
setName(in.readUTF());
Externalize in CBase Classs
out.writeInt(getInstanceCount()); // getting number of objects in the vector
for (int index = 0; index < getInstanceCount(); index++) {
out.writeObject((CHelper)vData.get(index));
out.writeBoolean(bWrite);
out.writeBoolean(bRead);
And reading like this::
int iNumberofInstances; // number of instances
iNumberofInstances = in.readInt();
vData.clear();
// Get the CHelper object and add it into the CFeatureBase vector
for (int i = 0; i < iNumberofInstances; i++) {
CHelper objbase = (CHelper)in.readObject();
vData.add(objbase);
this.bWrite = in.readBoolean();
this.bRead = in.readBoolean();
I suppose that i am correctly WRITING AND READING the data. B/w what can be the problem. All suggestions are welcome/
Thanks,
RohitVector = com.sp.CHelper@14b7453 // WHAT IS THIS ??
GARBAGE ?? NOT ABLE TO ANALYSE :(
Write = true;
Read = true;************
MY PROBLEM:
i.e How to get the information stored in the Vector
? I am getting something "com.sp.CHelper@14b7453 "
whereas the expected output should be Vector =
HELPER.This is perfectly normal behaviour if your CHelper class doesn't override the toString() method. The default toString() method in Object just prints the name of the class and a hex representation of the object's hash code.
Add a toString() method to CHelper that returns szname and you should see what you are expecting. -
User-Defined Data Type (Data Transfer Objects) is null
hi
i try to access a nested complex datatype over blazeds. i always see that the second level of the complex datatye is NULL but the other data's like String are ok.
here an example:
as you can see TT1 has a member TT2, and a String
TT2 has a member TT3 and a string
and TT3 has just a string.
in the ActionScript the TT2 referenz in TT1 is always NULL.
Java Code
Java code:
package clientreportingserver;
public class TT1 {
public String getT1s() {
return t1s;
public void setT1s(String t1s) {
this.t1s = t1s;
String t1s;
public TT2 getTt2() {
return tt2;
public void setTt2(TT2 tt2) {
this.tt2 = tt2;
TT2 tt2;
=================================================
package clientreportingserver;
public class TT2 {
public String getT2s() {
return t2s;
public void setT2s(String t2s) {
this.t2s = t2s;
String t2s;
public TT3 getTt3() {
return tt3;
public void setTt3(TT3 tt3) {
this.tt3 = tt3;
TT3 tt3;
=================================================
package clientreportingserver;
public class TT3 {
public String getT3s() {
return t3s;
public void setT3s(String t3s) {
this.t3s = t3s;
String t3s;
ActionScript DataType
package clientreporting.model
import mx.collections.ArrayCollection;
[RemoteClass(alias="clientreportingserver.TT1")]
[Bindable]
public class TT1
public var t1s:String;
public var t2:TT2
====================================================================
package clientreporting.model
import mx.collections.ArrayCollection;
[RemoteClass(alias="clientreportingserver.TT2")]
[Bindable]
public class TT2
public var t2s:String;
public var t2:TT3
===================================================================
package clientreporting.model
import mx.collections.ArrayCollection;
[RemoteClass(alias="clientreportingapi.TT3")]
[Bindable]
public class TT3
public var t3s:String;
here the output from blazeds. for me it looks perfect, all data are transmitted
BlazeDs output
[BlazeDS]Deserializing AMF/HTTP request
Version: 3
(Message #0 targetURI=null, responseURI=/5)
(Array #0)
[0] = (Typed Object #0 'flex.messaging.messages.RemotingMessage')
source = null
operation = "getTT"
destination = "exposedServiceWrapper"
clientId = "E57066B1-170E-503A-D4EC-004166E95FC3"
body = (Array #1)
timeToLive = 0
headers = (Object #2)
DSEndpoint = "channel-amf"
DSId = "E570490A-C218-4A29-4229-8CD6F29222FC"
timestamp = 0
messageId = "5FDB47AD-9066-DD95-4CD4-0CE0D3F6C337"
[BlazeDS]Adapter 'java-object' called 'null.getTT(java.util.Arrays$ArrayList (Collection size:0)
[BlazeDS]Result: 'clientreportingserver.TT1
t1s = TT 1 String
tt2 = clientreportingserver.TT2
t2s = TT 2 String
tt3 = clientreportingserver.TT3
t3s = TT 3 String
[BlazeDS]Serializing AMF/HTTP response
Version: 3
(Message #0 targetURI=/5/onResult, responseURI=)
(Externalizable Object #0 'DSK')
(Typed Object #1 'clientreportingserver.TT1')
t1s = "TT 1 String"
tt2 = (Typed Object #2 'clientreportingserver.TT2')
t2s = "TT 2 String"
tt3 = (Typed Object #3 'clientreportingserver.TT3')
t3s = "TT 3 String"
1.262936445958E12
(Byte Array #4, Length 16)
(Byte Array #5, Length 16)
(Byte Array #6, Length 16)
the last Alert (accessing t2) got always a null pointer, the fist Alert works fine and print out the string i expected to see
call in ActionScript
public function loadTT():void {
_policyService.getTT(loadTTHandle);
private function loadTTHandle(content:TT1):void {
Alert.show("" + content.t1s);
Alert.show("" + content.t2.t2s);
is it possible to access a nested complex type? i only could find simple examples.
thanks for your help joeI found the problem
in the flashlog.txt i found :ReferenceError: Error #1056: Cannot create property AFoo on [...]
this let me to this blog http://blog.comtaste.com/java/ ==>
Automating ActionScript 3 classes generation from Java Beans in a LiveCycle Data Services context
it was a naming problem. -
Explicity mapping between ActionScript and Java objects for the BlazeDS Messaging Service
The BlazeDS documentation shows how to explicitly map between ActionScript and Java objects. For example, this works fine for RPC services, e.g.
import flash.utils.IExternalizable;
import flash.utils.IDataInput;
import flash.utils.IDataOutput;
[Bindable]
[RemoteClass(alias="javaclass.User")]
public class User implements IExternalizable {
public var id : String;
public var secret : String;
public function User() {
public function readExternal(input : IDataInput) : void {
id = input.readObject() as String;
public function writeExternal(output : IDataOutput) : void {
output.writeObject(id);
and
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class User implements Externalizable {
protected String id;
protected String secret;
public String getId() {
return id;
public void setId(String id) {
this.id = id;
public String getSecret() {
return secret;
public void setSecret(String secret) {
this.secret = secret;
public void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException {
id = (String) in.readObject();
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(id);
If I called an RPC service that returns a User, the secret is not sent over the wire. Is it also possible to do this for the messaging service? That is, if I create a custom messaging adapter and use the function below, can I also prevent secret from being sent?
MessageBroker messageBroker = MessageBroker.getMessageBroker(null);
AsyncMessage message = new AsyncMessage();
message.setDestination("MyMessagingService");
message.setClientId(UUIDUtils.createUUID());
message.setMessageId(UUIDUtils.createUUID());
User user = new User();
user.setId("id");
user.setSecret("secret");
message.setBody(user);
messageBroker.routeMessageToService(message, null);Hi Martin. The way that AMF serialization/deserialization works for BlazeDS is the same regardless of which service is being used, so yes that code will work for messaging as well. On the server, the serialization/deserialization of messages happens at the endpoint. For an incoming message for example, the endpoint deserializes the message and then hands it off to the MessageBroker which decides which service/destination to deliver the message to.
That was a good question. Thanks for asking it. Lots of people are used to doing custom serialization/deserialization with the RPC services (RemoteObject/RemotingService) but I'm not sure everyone realizes they can do this for messaging as well.
-Alex -
How to Convert Externalizable objec to Serializable at Run Time
Urgent Help needed ...
Original Problem
We are using Externalizable object through the system .We keep track of versioning with field in each object ...
This version is common software version...
We change that version every two or three week ...
suppose for
My software Version is 2
and suppose if someone add a new field to any object we put a checking in readExternal to not read that field is the version is less than 2
Now the problem is say
1)We make a version 2 on June 10
2)Some one save a object on June 11
3)Some one add a new field in any object and put the checking of Version 2
4)Now object saved in step 2 cannot be read once step 3 is performed...
How do we solve this issue ?
We do not intend to add the class Version in each class at this stage. ...
Issue 2
Other issue related to issue 1 itself
We plan to convern the object to xml using some technology of Xstream
Now issue 1 can be solved atlease if my all the object were of type Serializable and NOT EXTERNALIZABLE
So my Second Question is is there a way to convert a Externalizable object to Serialiable object just for my purpose when i m converting to xml and vice versa .....
Basicallly I want a way to remove that EXTERNALIZABLE Interface from class at run time WITHOUT CHANGING ANY CLASS SOURCE CODE ...../
Guy Your help is very appreiciated to solve this two issue
Message was edited by:
palanmaheshIt seem i have not properly explained to you guys my
problem
I cannot change the code of existing object which are
Externalizable..
Nor can i solve the versioning issue That block of
source code i cannot touch
all i want is somehow convert the object so that they
are not instance of Externalizable Well it seems pretty hopeless then doesn't it.
My advice? Give up. You seem pretty lost and when people respond by basically saying they can't solve the problem in any reasonable way then it's pretty much a lost cause.
Good luck with your hopeless task. I see nothing but upset in your future. -
Export Aperture library objects to Finder folders
Inspired by the posted message by user http://discussions.apple.com/profile.jspa?userID=616539 I wrote a script that will do the following:
1. Sync Folders/SubFolders/Albums/Projects..etc hierarchy tree from Aperture to a Finder folders tree
2. At run-time the script will creates/append to a log file under (home directory)\Library\Logs\AppleScriptExportApertureLibrary.log
3. Exports all projects images versions with embedded metadata
4. Create a file system "hard links" for each photo in the respective Albums pointing to the project folder location in order to preserve space.
5. Compare modification date on the image files and modification date within Aperture and export only modified images in order to save time for a full sync/export.
6. In interactive mode you can select Export Folder location, Aperture Library location and Projects List (project list will contain project name and internal aperture project id).
7. In non-interactive (from command line) mode there are two arguments:
7a. "quiet" - exports all projects
7b. "quiet" "Project Information" - export only certain projects. the project information can be found in the log file.
8. Remove any images or folders from Finder export directory that do not exist in Aperture database any more.
Hopefully this would help anyone who is looking to export from Aperture on a regular basis. I am not sure how it will handle large amount of photos. I tested it with 3k+ photos.
Please note that you use this script at your own risk.
Here is the script code:
global theFoldersTree_G
global theLibraryPath_G
global theProcessedProjects_G
global theSelectedProjects_G
global theAllAlbums_G
global theScriptName_G
global Sqlite_G
global ApertureLibrary_G
global numExports_G
on run argv
set Sqlite_G to "/usr/bin/sqlite3"
set ApertureLibraryPath to POSIX path of (path to home folder) & "Pictures/"
set ApertureLibrary_G to ApertureLibraryPath & "Aperture Library.aplibrary/Aperture.aplib/Library.apdb"
set exportFolder to POSIX path of (path to home folder) & "Pictures/Aperture Exports/"
set theScriptName_G to "ExportApertureLibrary"
set theSelectedProjects_G to "ALL PROJECTS"
set theFoldersTree_G to {}
set theProcessedProjects_G to {}
set theAllAlbums_G to {}
set numExports_G to 0
logEvent("Started")
set theArgv1 to {}
set theArgv2 to {}
if (count of argv) ≥ 1 then
set theArgv1 to item 1 of argv
end if
if (count of argv) = 2 then
set theArgv2 to item 2 of argv
end if
logEvent("Passed ARGV 1: " & theArgv1)
logEvent("Passed ARGV 2: " & theArgv2)
if theArgv1 is not equal to "quiet" then
set theFile to (choose file with prompt "Please choose the Aperture Library file" default location POSIX file ApertureLibraryPath) as string
set ApertureLibrary_G to POSIX path of theFile & "Aperture.aplib/Library.apdb"
set exportFolder to POSIX path of (choose folder with prompt "Please choose the export folder" default location POSIX file exportFolder) as string
end if
logEvent("ApertureLibrary_G path is set to: " & ApertureLibrary_G)
logEvent("exportFolder path is set to: " & exportFolder)
try
tell application "Aperture"
logEvent("Getting list of project path information...") of me
set SqlStatement to "
select replace(A.ZLIBRARYRELATIVEPATH,'.approject',''),A.ZUUID
from ZRKFOLDER AS A
WHERE A.ZFOLDERTYPE=2
ORDER BY A.ZNAME"
set SQLProjectUUIDPath to DB_execute(SqlStatement) of me
set theProjectsOptions to SQLProjectUUIDPath as list
set end of theProjectsOptions to "ALL PROJECTS"
logEvent("Projects list: " & theProjectsOptions as string) of me
if theArgv1 is not equal to "quiet" then
set theSelectedProjects_G to choose from list SQLProjectUUIDPath with prompt "Please choose a project(s):"
end if
if theArgv2 is not equal to {} then
set theSelectedProjects_G to theArgv2
end if
logEvent("The selected projects : " & theSelectedProjects_G as string) of me
logEvent("Getting list of libraries...") of me
set theLibraryList to every library
logEvent("Found " & (count of theLibraryList) & " libraries") of me
repeat with theLibrary in theLibraryList
set theLibraryName to name of theLibrary
logEvent("Processing library: " & theLibraryName) of me
set LibraryFolders to {}
set theProcessedProjects_G to {}
tell application "Finder"
if not (exists (POSIX file (exportFolder & theLibraryName) of me)) then
logEvent("creating new folder " & theLibraryName & " at " & exportFolder) of me
make new folder at (POSIX file exportFolder of me) with properties {name:theLibraryName}
end if
end tell
set theLibraryPath_G to exportFolder & theLibraryName & "/"
logEvent("Getting list of folders...") of me
set theFolderList to every folder of library id (id of theLibrary)
logEvent("Found " & (count of theFolderList) & " folders") of me
set theRootFolderList to {}
if theFolderList is not equal to {} then
processFoldersTree(0, theFolderList) of me
repeat with theFolder in theFolderList
if (id of theFolder) is not in theFoldersTree_G as string then
logEvent("Found root folder : " & (name of theFolder) as string) of me
set end of theRootFolderList to theFolder
set end of LibraryFolders to (name of theFolder)
end if
end repeat
end if
if theRootFolderList is not equal to {} then
processFolders(theRootFolderList, theLibraryPath_G, "projects") of me
processFolders(theRootFolderList, theLibraryPath_G, "albums") of me
else
set theProjectList to every project of library id (id of theLibrary)
set end of LibraryFolders to processProjects(theProjectList, theLibraryPath_G, "projects") of me as list
processProjects(theProjectList, theLibraryPath_G, "albums") of me
end if
logEvent("Getting list of projects...") of me
set theProjectList to every project of library id (id of theLibrary)
logEvent("Found " & (count of theProjectList) & " projects") of me
logEvent("Getting list of albums...") of me
set theAlbumList to every album of library id (id of theLibrary)
logEvent("Found " & (count of theAlbumList) & " albums") of me
set theRootProjectList to {}
if theProjectList is not equal to {} then
repeat with theProject in theProjectList
if (id of theProject) is not in theProcessedProjects_G as string then
logEvent("Found root project : " & (name of theProject) as string) of me
set end of theRootProjectList to theProject
set end of LibraryFolders to (name of theProject)
end if
end repeat
end if
set theRootAlbumList to {}
if theAlbumList is not equal to {} then
processAlbumsTree(theProjectList, theFolderList) of me
set theRootAlbumList to {}
repeat with theAlbum in theAlbumList
if (id of theAlbum) is not in theAllAlbums_G as string then
logEvent("Found root album : " & (name of theAlbum) as string) of me
set end of theRootAlbumList to theAlbum
set end of LibraryFolders to (name of theAlbum)
end if
end repeat
end if
if theRootProjectList is equal to {} then
processAlbums(theRootAlbumList, theLibraryPath_G, "albums") of me
else
processProjects(theRootProjectList, theLibraryPath_G, "projects") of me
processProjects(theRootProjectList, theLibraryPath_G, "albums") of me
if theRootAlbumList is not equal to {} then
processAlbums(theRootAlbumList, theLibraryPath_G, "albums") of me
end if
end if
cleanup(LibraryFolders, theLibraryPath_G, "all") of me
end repeat
logEvent("total exports : " & numExports_G) of me
if theArgv1 is not equal to "quiet" then
display dialog "Total image exports : " & numExports_G buttons {"OK"} with title "Aperture Library Export" with icon note
end if
end tell
on error s number i partial result p from f to t
set s to "Error: " & s
logEvent(quoted form of (s))
if theArgv1 is not equal to "quiet" then
display dialog "ERROR : " & s buttons {"OK"} with title "Aperture Library Export" with icon note
end if
end try
end run
on cleanup(theObjects, thePath, theSelection)
logEvent("Cleaning export folders...") of me
logEvent("# Objects: " & (count of theObjects)) of me
logEvent("Export Folder: " & (thePath as string))
tell application "Finder"
logEvent("Getting list of folders...") of me
set theFolderList to every folder in folder (POSIX file thePath of me)
logEvent("Found " & (count of theFolderList) & " folders") of me
logEvent("Getting list of files...") of me
set theFileList to every file in folder (POSIX file thePath of me)
logEvent("Found " & (count of theFileList) & " files") of me
repeat with theFolder in theFolderList
set theFolderName to name of theFolder
if theFolderName is not in theObjects as string then
logEvent("Moving folder " & theFolder & " to trash...") of me
move theFolder to trash
end if
end repeat
if theSelection is not equal to "folder" then
repeat with theFile in theFileList
set theFileName to name of theFile
if theFileName is not in theObjects as string then
logEvent("Moving file " & theFile & " to trash...") of me
move theFile to trash
end if
end repeat
end if
end tell
logEvent("Cleaning completed...") of me
end cleanup
on logEvent(logMessage)
set theLine to quoted form of (((current date) as string) ¬
& " : " & logMessage)
do shell script "echo " & theLine & ¬
" >> ~/Library/Logs/AppleScript" & theScriptName_G & ".log"
end logEvent
on DB_lookupProjectPath(puuid)
set SqlStatement to "
select replace(rtrim(ZLIBRARYRELATIVEPATH,'.approject'),'/',':' )
from ZRKFOLDER
where
ZUUID ='" & puuid & "'"
set SqlRecords to DB_execute(SqlStatement)
return DB_record(SqlRecords, 1, 1)
end DB_lookupProjectPath
on processFolders(theFoldersList, theFolderPath, processOrder)
logEvent("processFolders... : " & theFolderPath) of me
set arrayOfFolders to {}
tell application "Aperture"
set theCount to count of theFoldersList
set theCounter to 1
repeat with theFolder in theFoldersList
set foldersOfFolder to {}
set theFolderName to name of theFolder
logEvent("Processing folder : " & theFolderName & " (" & theCounter & "/" & theCount & ")") of me
set theCounter to theCounter + 1
set end of arrayOfFolders to theFolderName
tell application "Finder"
if not (exists (POSIX file (theFolderPath & theFolderName) of me)) then
logEvent("creating new folder " & theFolderName & " at " & theFolderPath) of me
make new folder at (POSIX file theFolderPath of me) with properties {name:theFolderName}
end if
end tell
logEvent("Getting list of album...") of me
set theAlbumsListOfFolder to every album of folder id (id of theFolder)
logEvent("Found " & (count of theAlbumsListOfFolder) & " albums") of me
logEvent("Getting list of folder...") of me
set theFolderListOfFolder to every folder of folder id (id of theFolder)
logEvent("Found " & (count of theFolderListOfFolder) & " folders") of me
logEvent("Getting list of project...") of me
set theProjectsListOfFolder to every project of folder id (id of theFolder)
logEvent("Found " & (count of theProjectsListOfFolder) & " projects") of me
if theProjectsListOfFolder is not equal to {} then
set end of foldersOfFolder to processProjects(theProjectsListOfFolder, (theFolderPath & theFolderName & "/"), processOrder) of me as list
end if
if theFolderListOfFolder is equal to {} then
set end of foldersOfFolder to processAlbums(theAlbumsListOfFolder, (theFolderPath & theFolderName & "/"), processOrder) of me as list
else
if theAlbumsListOfFolder is not equal to {} then
set end of foldersOfFolder to processAlbums(theAlbumsListOfFolder, (theFolderPath & theFolderName & "/"), processOrder) of me as list
end if
set end of foldersOfFolder to processFolders(theFolderListOfFolder, (theFolderPath & theFolderName & ":"), processOrder) of me as list
end if
cleanup(foldersOfFolder, (theFolderPath & theFolderName & "/"), "all") of me
end repeat
end tell
logEvent("processFolders completed...") of me
return arrayOfFolders
end processFolders
on processFoldersTree(theParent, theFoldersList)
logEvent("processFoldersTree...") of me
tell application "Aperture"
repeat with theFolder in theFoldersList
if theParent is not 0 then
set end of theFoldersTree_G to (id of theFolder)
end if
set theFolderListOfFolder to every folder of folder id (id of theFolder)
if theFolderListOfFolder is not equal to {} then
processFoldersTree((id of theFolder), theFolderListOfFolder) of me
end if
end repeat
end tell
logEvent("processFoldersTree completed...") of me
end processFoldersTree
on processAlbumsTree(theProjectsList, theFoldersList)
logEvent("processAlbumsTree...") of me
set theAllAlbums_G to {}
tell application "Aperture"
repeat with theProject in theProjectsList
repeat with theAlbum in (every album of project id (id of theProject))
set end of theAllAlbums_G to (id of theAlbum)
end repeat
end repeat
repeat with theProject in theProjectsList
repeat with theAlbum in (every album of every subfolder of project id (id of theProject))
set end of theAllAlbums_G to (id of theAlbum)
end repeat
end repeat
repeat with theFolder in theFoldersList
repeat with theAlbum in (every album of folder id (id of theFolder))
set end of theAllAlbums_G to (id of theAlbum)
end repeat
end repeat
end tell
logEvent("processAlbumsTree completed...") of me
end processAlbumsTree
on DB_lookupImageInfo(puuid)
set SqlStatement to "
SELECT replace(A.ZLIBRARYRELATIVEPATH,'.approject',''),strftime('%m/%d/%Y %H:%M:%S',datetime(B.ZDATELASTSAVEDINDATABASE, 'unixepoch', '+31 years','localtime'))
from ZRKFOLDER AS A,ZRKVERSION AS B
where
B.ZPROJECTUUID = A.ZUUID AND
B.ZUUID ='" & puuid & "'"
set SqlRecords to DB_execute(SqlStatement)
return {theImagePath:DB_record(SqlRecords, 1, 1), theImageDate:DB_record(SqlRecords, 1, 2)}
end DB_lookupImageInfo
on processProjects(theProjectsList, theProjectPath, processOrder)
logEvent("processProjects... : " & theProjectPath) of me
logEvent("processOrder: " & processOrder) of me
set arrayOfProjects to {}
tell application "Aperture"
set theCount to count of theProjectsList
set theCounter to 1
repeat with theProject in theProjectsList
set ProjectFolders to {}
set theProjectName to name of theProject
set end of arrayOfProjects to theProjectName
set theContinue to 0
if "ALL PROJECTS" is not in theSelectedProjects_G as string then
if (id of theProject) is not in theSelectedProjects_G as string then
logEvent("Skipping project: " & theProjectName) of me
set theContinue to 1
end if
end if
if theContinue = 0 then
-- set theProjectPath to theProjectPath & DB_lookupProjectPath(id of theProject) of me
logEvent("Processing project : " & theProjectName & " (" & theCounter & "/" & theCount & ")") of me
tell application "Finder"
if not (exists (POSIX file (theProjectPath & theProjectName) of me)) then
logEvent("creating new folder " & theProjectName & " at " & theProjectPath) of me
make new folder at (POSIX file theProjectPath of me) with properties {name:theProjectName}
end if
end tell
if processOrder is equal to "projects" then
set end of theProcessedProjects_G to (id of theProject)
logEvent("Getting list of images...") of me
set theImageList to every image version of project id (id of theProject) as list
logEvent("Found " & (count of theImageList) & " images") of me
set end of ProjectFolders to processImages(theImageList, (theProjectPath & theProjectName & "/"), "project", "JPEG - Original Size") of me as list
end if
logEvent("Getting list of subfolders...") of me
set theSubfolderList to every subfolder of project id (id of theProject)
logEvent("Found " & (count of theSubfolderList) & " subfolders") of me
logEvent("Getting list of album...") of me
set theAlbumList to every album of project id (id of theProject)
logEvent("Found " & (count of theAlbumList) & " albums") of me
if theSubfolderList is equal to {} then
set end of ProjectFolders to processAlbums(theAlbumList, (theProjectPath & theProjectName & "/"), processOrder) of me as list
else
if theAlbumList is not equal to {} then
set end of ProjectFolders to processAlbums(theAlbumList, (theProjectPath & theProjectName & "/"), processOrder) of me as list
end if
set end of ProjectFolders to processSubfolders(theSubfolderList, (theProjectPath & theProjectName & "/"), processOrder) of me as list
end if
if processOrder is equal to "projects" then
cleanup(ProjectFolders, (theProjectPath & theProjectName & "/"), "all") of me
else
cleanup(ProjectFolders, (theProjectPath & theProjectName & "/"), "folder") of me
end if
end if
set theCounter to theCounter + 1
end repeat
end tell
logEvent("processProjects completed...") of me
return arrayOfProjects
end processProjects
on exportImage(imageUUID, imageName, exportFolder, imageType, isAlbum, exportSettings, imageExt)
set imageInfo to DB_lookupImageInfo(imageUUID) of me
set imageTime to theImageDate of imageInfo as Unicode text
set imageDate to date imageTime
set isExported to 0
set imageName to imageName & imageExt
set theFile to POSIX file (exportFolder & imageName)
tell application "Finder"
set isUpdate to 0
if not (exists theFile) then
logEvent("Image does not exist in folder : " & exportFolder) of me
set isUpdate to 1
else
logEvent("Get image modification date") of me
do shell script "ls -l " & quoted form of (exportFolder & imageName)
set imageFileDate to modification date of (info for theFile)
if imageDate ≥ imageFileDate then
logEvent("Image file date: " & imageFileDate as string) of me
set isUpdate to 1
end if
end if
if isUpdate = 1 then
if exists theFile then
move theFile to the trash
end if
if isAlbum is equal to "album" then
logEvent("Creating a link for image: " & imageName) of me
set theProjectFolder to theImagePath of imageInfo
do shell script "ln " & quoted form of (theLibraryPath_G & theProjectFolder & "/" & imageName) & " " & quoted form of exportFolder
else
set isExported to 1
logEvent("Exporting image: " & imageName) of me
tell application "Aperture"
if imageType is "master" then
set settings to exportSettings
export {image version id imageUUID} using settings to POSIX path of file exportFolder metadata embedded
else
set settings to exportSettings
export {image version id imageUUID} using settings to POSIX path of file exportFolder metadata embedded
end if
end tell
end if
end if
end tell
return isExported
end exportImage
on DB_execute(SqlStatement)
try
set SqlScript to Sqlite_G & space & "-separator '|'" & space & (quoted form of ApertureLibrary_G) & space & (quoted form of SqlStatement) & " 2>&1"
set SqlResult to do shell script SqlScript
set theARray to {}
set tid to AppleScript's text item delimiters
set AppleScript's text item delimiters to ASCII character 13
set SqlRecords to text items of SqlResult
set AppleScript's text item delimiters to tid
return SqlRecords
on error s number i partial result p from f to t
set s to "SQL Error: " & s
logEvent(quoted form of (s)) of me
end try
end DB_execute
on DB_record(SqlRecords, row, col)
try
set tid to AppleScript's text item delimiters
set AppleScript's text item delimiters to "|"
set SqlCols to text items of (item row of SqlRecords)
set AppleScript's text item delimiters to tid
return item col of SqlCols
on error
return {}
end try
end DB_record
on DB_lookupRecord(SqlRecords, theReturnCol, theText, theCol)
set theResult to ""
repeat with theRow in items of SqlRecords
set tid to AppleScript's text item delimiters
set AppleScript's text item delimiters to "|"
set theFields to text items of theRow
set AppleScript's text item delimiters to tid
if item theCol of theFields is equal to theText then
set theResult to item theReturnCol of theFields
exit repeat
end if
end repeat
return theResult
end DB_lookupRecord
on processImages(theImageList, exportFolder, isAlbum, exportSettings)
logEvent("processImages... : " & exportFolder) of me
if exportSettings contains "JPEG" then
set imageExt to ".jpg"
else
set imageExt to ".ANY"
end if
set arrayOfImages to {}
with timeout of 6000 seconds
set theCount to count of theImageList
set theCounter to 1
repeat with theImage in theImageList
set imageUUID to id of theImage
set imageName to name of theImage
set end of arrayOfImages to imageName & imageExt
logEvent("Processing image : " & imageName & " (" & theCounter & "/" & theCount & ")") of me
set numExports_G to numExports_G + (exportImage(imageUUID, imageName, exportFolder, "version", isAlbum, exportSettings, imageExt) of me)
set theCounter to theCounter + 1
end repeat
end timeout
return arrayOfImages
end processImages
on processAlbums(theAlbumsList, theAlbumPath, processOrder)
logEvent("processAlbums... : " & theAlbumPath) of me
set arrayOfAlbums to {}
tell application "Aperture"
set theCount to count of theAlbumsList
set theCounter to 1
repeat with theAlbum in theAlbumsList
set theAlbumName to name of theAlbum
logEvent("Processing album : " & theAlbumName & " (" & theCounter & "/" & theCount & ")") of me
set theCounter to theCounter + 1
set AlbumObjects to {}
set end of arrayOfAlbums to theAlbumName
tell application "Finder"
if not (exists (POSIX file (theAlbumPath & theAlbumName) of me)) then
logEvent("creating new folder " & theAlbumName & " at " & theAlbumPath) of me
make new folder at (POSIX file theAlbumPath of me) with properties {name:theAlbumName}
end if
end tell
logEvent("Getting list of images...") of me
set theImagesList to every image version of album id (id of theAlbum)
logEvent("Found " & (count of theImagesList) & " images") of me
if processOrder is equal to "albums" then
set end of AlbumObjects to processImages(theImagesList, (theAlbumPath & theAlbumName & "/"), "album", "JPEG - Original Size") of me as list
cleanup(AlbumObjects, (theAlbumPath & theAlbumName & "/"), "all") of me
end if
end repeat
end tell
logEvent("processAlbums completed...") of me
return arrayOfAlbums
end processAlbums
on processSubfolders(theSubfoldersList, theSubfolderPath, processOrder)
logEvent("processSubfolders... : " & theSubfolderPath) of me
set arrayOfSubfolders to {}
tell application "Aperture"
set theCount to count of theSubfoldersList
set theCounter to 1
repeat with theSubfolder in theSubfoldersList
set theSubfolderName to name of theSubfolder
logEvent("Processing subfolder : " & theSubfolderName & " (" & theCounter & "/" & theCount & ")") of me
set theCounter to theCounter + 1
set end of arrayOfSubfolders to theSubfolderName
tell application "Finder"
if not (exists (POSIX file (theSubfolderPath & theSubfolderName) of me)) then
logEvent("creating new folder " & theSubfolderName & " at " & theSubfolderPath) of me
make new folder at (POSIX file theSubfolderPath of me) with properties {name:theSubfolderName}
end if
end tell
set SubfoldersFolders to {}
logEvent("Getting list of albums ...") of me
set theAlbumsListOfSubfolder to every album of subfolder id (id of theSubfolder)
logEvent("Found " & (count of theAlbumsListOfSubfolder) & " albums") of me
logEvent("Getting list of subfolders...") of me
set theSubfoldersListOfSubfolder to every subfolder of subfolder id (id of theSubfolder)
logEvent("Found " & (count of theSubfoldersListOfSubfolder) & " subfolders") of me
if theSubfoldersListOfSubfolder is equal to {} then
set end of SubfoldersFolders to processAlbums(theAlbumsListOfSubfolder, (theSubfolderPath & theSubfolderName & "/"), processOrder) of me as list
else
if theAlbumsListOfSubfolder is not equal to {} then
set end of SubfoldersFolders to processAlbums(theAlbumsListOfSubfolder, (theSubfolderPath & theSubfolderName & "/"), processOrder) of me as list
end if
set end of SubfoldersFolders to processSubfolders(theSubfoldersListOfSubfolder, (theSubfolderPath & theSubfolderName & "/"), processOrder) of me as list
end if
cleanup(SubfoldersFolders, (theSubfolderPath & theSubfolderName & "/"), "all") of me
end repeat
end tell
logEvent("processSubfolders completed...") of me
return arrayOfSubfolders
end processSubfoldersIf you do externalize your Masters to folders anywhere (same drive, internal/external drive, multiple drives, whatever), never be tempted to use Finder to mess with them.
As Frank said, use Relocate Masters. Otherwise you'll confuse Aperture when it wakes up expecting Masters to be in certain places when they have moved elsewhere.
It's possible to fix up the mess, but it's no fun! -
Drag and Drop objects with JavaFX properties
Has anyone tried to implement drag and drop functionality with objects containing JavaFX properties? The issue I'm running into is that the objects appear to need to be serializable, and the JavaFX properties are not serializable. I can implement Externalizable instead of Serializable, and basically serialize the values contained by the FX properties, but of course I lose any bindings by doing this (as the listeners underlying the bindings are not serialized).
The [url http://docs.oracle.com/javafx/2/api/javafx/scene/input/Clipboard.html]javadocs for Clipboard state "In addition to the common or built in types, you may put any arbitrary data onto the clipboard (assuming it is either a reference, or serializable. See more about references later)" but I don't see any further information about references (and at any rate, this doesn't really make sense since anything that's serializable would be a reference anyway). Is there a special DataFormat I can use to specify a reference in the local JVM so the reference gets passed without serialization?
I know this might not make sense without a code example; I just thought someone might have come across this closely enough to recognize the problem. I'll try to post a code example tonight...Here's pretty much the simplest example I can come up with. I have a real-world example of needing to do this, but the object model for the real example is quite a bit more complex.
For the toy example, imagine we have a list of projects, some employees, and we want to assign each project to one or more employees. I have a Project class with a title and assignee. (In real life you can imagine other properties; due date, status, etc.) I'll keep a ListView with a bunch of unassigned projects (titles but assignees equal to null) and then a ListView for each of the employees.
To assign a project to an employee, the user should be able to drag from the "master" list view to one of the employee list views. When the project is dropped, we'll create a new project, set the assignee, and bind the title of the new project to the title of the project which was dragged. (Remember we want to be able to assign a single project to multiple employees.)
So here's the first shot:
A couple of simple model classes
Project.java
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
public class Project {
private final StringProperty title ;
private final ObjectProperty<Employee> assignee ;
public Project(String title) {
this.title = new SimpleStringProperty(this, "title", title);
this.assignee = new SimpleObjectProperty<Employee>(this, "assignee");
public Project() {
this("");
public StringProperty titleProperty() {
return title ;
public String getTitle() {
return title.get() ;
public void setTitle(String title) {
this.title.set(title);
public ObjectProperty<Employee> assigneeProperty() {
return assignee ;
public Employee getAssignee() {
return assignee.get();
public void setAssignee(Employee assignee) {
this.assignee.set(assignee);
@Override
public String toString() {
String t = title.get();
Employee emp = assignee.get();
if (emp==null) {
return t;
} else {
return String.format("%s (%s)", t, emp.getName()) ;
}Employee.java
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
public class Employee {
private StringProperty name ;
public Employee(String name) {
this.name = new SimpleStringProperty(this,"name", name);
public StringProperty nameProperty() {
return name ;
public String getName() {
return name.get();
public void setName(String name) {
this.name.set(name);
}and the application
DnDExample.java
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.TextField;
import javafx.scene.input.ClipboardContent;
import javafx.scene.input.DataFormat;
import javafx.scene.input.DragEvent;
import javafx.scene.input.Dragboard;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.TransferMode;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Callback;
public class DnDExample extends Application {
private static final DataFormat PROJECT_DATA_FORMAT = new DataFormat("com.example.project"); // is there something special I can use here?
@Override
public void start(Stage primaryStage) {
final HBox root = new HBox(5);
final ListView<Project> allProjects = new ListView<Project>();
final Employee fred = new Employee("Fred");
final Employee ginger = new Employee("Ginger");
final ListView<Project> fredsProjects = new ListView<Project>();
final ListView<Project> gingersProjects = new ListView<Project>();
final VBox employeeLists = new VBox(5);
employeeLists.getChildren().addAll(new Label("Fred's Projects"), fredsProjects, new Label("Ginger's Projects"), gingersProjects);
root.getChildren().addAll(allProjects, employeeLists);
allProjects.setCellFactory(new Callback<ListView<Project>, ListCell<Project>>() {
@Override
public ListCell<Project> call(ListView<Project> listView) {
return new AllProjectListCell();
allProjects.setEditable(true);
final EventHandler<DragEvent> dragOverHandler = new EventHandler<DragEvent>() {
@Override
public void handle(DragEvent dragEvent) {
if (dragEvent.getDragboard().hasContent(PROJECT_DATA_FORMAT)) {
dragEvent.acceptTransferModes(TransferMode.COPY);
fredsProjects.setOnDragOver(dragOverHandler);
gingersProjects.setOnDragOver(dragOverHandler);
fredsProjects.setOnDragDropped(new DragDropHandler(fred, fredsProjects.getItems()));
gingersProjects.setOnDragDropped(new DragDropHandler(ginger, gingersProjects.getItems()));
allProjects.getItems().addAll(new Project("Implement Drag and Drop"), new Project("Fix serialization problem"));
Scene scene = new Scene(root, 600, 400);
primaryStage.setScene(scene);
primaryStage.show();
public static void main(String[] args) {
launch(args);
private class DragDropHandler implements EventHandler<DragEvent> {
private final Employee employee ;
private final ObservableList<Project> itemList;
DragDropHandler(Employee employee, ObservableList<Project> itemList) {
this.employee = employee ;
this.itemList = itemList ;
@Override
public void handle(DragEvent event) {
Dragboard db = event.getDragboard();
if (db.hasContent(PROJECT_DATA_FORMAT)) {
Project project = (Project) db.getContent(PROJECT_DATA_FORMAT);
Project assignedProject = new Project();
assignedProject.titleProperty().bind(project.titleProperty());
assignedProject.setAssignee(employee);
itemList.add(assignedProject);
private class AllProjectListCell extends ListCell<Project> {
private TextField textField ;
private final EventHandler<MouseEvent> dragDetectedHandler ;
AllProjectListCell() {
this.dragDetectedHandler = new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
Dragboard db = startDragAndDrop(TransferMode.COPY);
ClipboardContent cc = new ClipboardContent();
cc.put(PROJECT_DATA_FORMAT, getItem());
db.setContent(cc);
event.consume();
this.setEditable(true);
@Override
public void updateItem(final Project project, boolean empty) {
super.updateItem(project, empty);
if (empty) {
setText(null);
setGraphic(null);
setOnDragDetected(null);
} else if (isEditing()) {
if (textField != null) {
textField.setText(getItem().getTitle());
setText(null) ;
setOnDragDetected(null);
setGraphic(textField);
} else {
setText(project.getTitle());
setOnDragDetected(dragDetectedHandler);
@Override
public void startEdit() {
super.startEdit();
if (!isEmpty()) {
textField = new TextField(getItem().getTitle());
textField.setMinWidth(this.getWidth()-this.getGraphicTextGap());
textField.focusedProperty().addListener(new ChangeListener<Boolean>() {
@Override
public void changed(
ObservableValue<? extends Boolean> observable,
Boolean oldValue, Boolean newValue) {
if (! newValue) {
getItem().setTitle(textField.getText());
commitEdit(getItem());
setText(null);
setGraphic(textField);
setOnDragDetected(null);
@Override
public void cancelEdit() {
super.cancelEdit();
if (!isEmpty()) {
setText(getItem().getTitle());
setGraphic(null);
setOnDragDetected(dragDetectedHandler);
} else {
setText(null);
setGraphic(null);
setOnDragDetected(null);
@Override
public void commitEdit(Project project) {
super.commitEdit(project);
if (!isEmpty()) {
setText(getItem().getTitle());
setGraphic(null);
setOnDragDetected(dragDetectedHandler);
}If you try to drag from the list on the left to one of the lists on the right, it will fail pretty quickly and tell you that the data need to be Serializable.
Simply adding "implements Serializable" to the Project and Employee classes doesn't work, as you find that SimpleStringProperty and SimpleObjectProperty are not Serializable. So instead I can use Externalizable:
public class Project implements Externalizable {
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(title.get());
out.writeObject(assignee.get());
@Override
public void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException {
setTitle((String)in.readObject());
setAssignee((Employee)in.readObject());
}and
public class Employee implements Externalizable {
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(name.get());
@Override
public void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException {
setName((String) in.readObject());
}This makes the drag and drop work, but if you drop a project on one of the employee lists, then edit the project title in the master list, the binding is not respected. This is because deserialization creates a new SimpleStringProperty for the title, which is not the property to which the title of the new Project object is bound.
What I really want to do is to be able to drag and drop an object within the same JVM simply by passing the object by reference, rather than by serializing it. Is there a way to do this? Is there some DataFormat type I need? -
Externalizable vs DataOutputStream/DataInputStream (Storable Interface)
I have created an interface Storable as below which I use to save a complex Object to a byte array and ultimately to a database. The total processing time is 1hour and 5 minutes.
public interface Storable
void readData(DataInputStream in) throws IOException;
void writeData(DataOutputStream out) throws IOException;
I then swapped to Externalizable.
I changed the top Object readData/writeData functions to
readExternal and writeExternal.
Then I added new readData/writeData functions to the contained Objects
as below - so these objects are "new"ed and filled in by my code.
void readData(InputObject in) throws IOException;
void writeData(OutputObject out) throws IOException;
Total processing time was reduced to 33minutes.
Can anybody explain this >50% speed improvement?
Also I would rather not use Externalizable so I can decouple the persisted data away from the Java code but it looks like one heck of a time penalty for doing so.The point is the only difference between the code I have written is that one is using
ObjectInput/ObjectOutput (Externalizable) (34 minutes)
the other is using
DataOutputStream/DataInputStream (Storable) (1hour 5 minutes)
and my code for the 2 methods is basically the same.
This would lead to the conclusion that one of or both Java classes DataOutputStream/DataInputStream are very inefficient and I want to know why?
Maybe you are looking for
-
I am having a problem starting the Oracle Listener after a system crash on Windows NT. However, if I first connect to the internet and then try to restart, everything works ok. Why should I have to connect to the internet with my laptop in order to s
-
Video doesn't slow when added to timeline of different frame rate
I'm having trouble understanding what's going on here. I have a video shot at 59.94 fps, and I add it to a sequence that is set at 29.97 fps. It plays back normally which makes sense if premiere is dropping frames by 1/2, but the drop frame indicat
-
What is the best way to work with avi-files?
Hallo all, I'm new to this forum so before asking my question I'd like to say some words about myself: I'm not a native english speaker, so please excuse language mistakes I'll possibly make and feel free to ask if I'll write something which you don'
-
AppLocker Policy to block Malware
I am in process of implementing Applocker in our Environment. To protect the clients from Malware attacks I want to configure a policy through which all the executable files can not run from User Profile. However I may have few executable files which
-
Hello I have set up a calculated field in a report to show the number of days between a job start and end date. I am using T-SQL: =DateDiff("D", fields!jobstartdate.value, fields!jobenddate.value) This works fine, but is of course including weekends