Programmers Guide to the
Observation Manager Java API 1.0


This document should be read by programmers who want to use or extend the Observation Manager Java API. The latest version of the API can always be downloaded at the projects main page. Please also see the JavaDoc pages which should be used as additional information source about the Java API.

Also please see the XML Standard documentation, as the standard is the basis on which the API is constructed. Latest version and HTML documentation for the XML Standard can be accessed here.


 1.0 Introduction

Prerequisite for using the Java API:
I won't give a "howto install" here for the above named software, therefore please see the corresponding documentation of the referred software.
Please use no prior version before Java 1.4 as the required java xml interfaces were introduced with 1.4. For the Apache Xerces you'll need to place these two jars: xercesImpl.jar, xml-apis.jar into your java classpath.
Apache Ant can be used to build the APIs jars or javadocs. The corresponding build.xml descriptions can be found in the Observation Manager Java API source distribution (see /build folder). Please use at least version 1.6 of Apache Ant.

License:

The whole Observation Manager Project (means all its distributions and sources) are copyrighted by the corresponding author and distributed under the Apache License 2.0.


 2.0 Coding standards and styles

These general naming conventions and method behaviors can be find across almost all classes of the Java API.

Setter methods that throw
java.lang.IllegalArgumentException
Some setter methods declare to throw a java.lang.IllegalArgumentException, even if this is not necessary as this exception is handled by java.
Then, why does the method tell you that it might throw the exception? The answer is easy: All mandatory fields that can be set over a setter method throw this exception. First this should help the programmer to directly see that the field is mandatory and also it ensures that only valid values can be set. If you try to set a non valid value (like
NULL) to a field that is mandatory, you end up with a java.lang.IllegalArgumentException. Please mind that the java.lang.IllegalArgumentException is an unchecked exception, means the compiler doesn't take care about it. If the value is somehow not OK, you'll find out during runtime!

Getter methods that return
Float.NaN or Double.NaN
If a getter methods returns you a Float.NaN (or Double.NaN) (NaN = Not a number) it just indicates that the field the getter belongs to is optional and wasn't set so far. So a unset optional field has a default value of NULL, Float.NaN or Double.NaN. Please be aware that you can't check for NaN values with the normal equals operator like:
float a = Float.NaN;
if( a == Float.Nan ) {
}
this won't work, as a is "not a number". Use this instead:
float a = Float.NaN;
if( Float.isNaN(a) ) {
}


The method:
addAsLinkToXmlElement(org.w3c.Element)
In many classes of the Java API you'll find a method like: addAsLinkToXmlElement(org.w3c.Element). So what is it used for? In most cases you shouldn't care about this method, as it is called automatically while serializing a whole de.lehmannet.om.Observations document. Nevertheless here's what this method does in an example:
Lets say you call this method on an instance of the class
de.lehmannet.om.Scope and pass the xml element <observation> as parameter.
What the method does is: It creates a new element
<scope> underneath <observation> with objects ID as value. (the ID is mostly some unique GUID).
Then it gets the parent document of
<observation> (which is normally: <fgca:observations>) and searches for the <scope> container element (which is called: <scopes>). If it doesn't find the container element, it creates it. Then it creates a new <scope> element underneath the container and writes the objects data inside the new element.
Seen from the XML document point of view the method call would look like this:
Before:
<fgca:observations>
 
<observation>
    Some more stuff that belongs to the observation, like observer, target....
 
<observation>
    More observations and container elements
</fgca:observations>
After:
<fgca:observations>
 
<observation>
    <scope>fabe9:fda997a012:-7fee</scope>
    Some more stuff that belongs to the observation, like observer, target....
 
<observation>
  <scopes>
    <scope id="
fabe9:fda997a012:-7fee">
      <model>Nikon 10x50 CF</model>
      <aperture>50.0</aperture>
      <magnification>10.0</magnification>
    </scope>
   </scopes>
    More observations and container elements
</fgca:observations>


 3.0 The Structure of the java aPI

The structure of the API derives from the XML Schema definition. The start element of the Java API is de.lehmannet.om.Observations, which corresponds with the root element in the XML Schema <fgca:observations>. The class de.lehmannet.om.Observations itself holds no data. It is more a container for all de.lehmannet.om.Observations classes which have been entered. Also it provides some XML Constants needed for serializing a document. The most interesting part of the class is the method: serializeAsXml(java.io.File xmlFile) which writes all de.lehmannet.om.Observations (and their subelements) in the given file as xml. Another interesting class is de.lehmannet.om.Observation it is the main class for data storage and corresponds with <observation> in the xml.  The class contains directly or indirectly all data which describes an astronomical observation.
If two instances of
de.lehmannet.om.Observation where made by the same observer (which is represented by de.lehmannet.om.Observer) both instances share the same class! This corresponds with the XML Schema definition, as both classes "link" to one and the same observer. The same applies to all other data that is stored in de.lehmannet.om.Observation (except for the "native" types like the begin date (= the start date of the observation). All other elements of the package de.lehmannet.om represent more or less all the elements described in the XML Standard definition.

The package
de.lehmannet.om.util contains helper and utilities classes, which are needed to load, format or convert data. One example is the class
de.lehmannet.om.util.DateConverter which is needed to transform a java.util.Date object into a ISO8601 string, that represents a date within the XML Standard. Additionally the class contains a parser, which can take a ISO8601 formated string and return a java.util.Date object. The conversion of a Gregorian date into a Julian date is also offered by the class. The class de.lehmannet.om.util.UIDGenerator is a simple global unique ID generator based on the java.rmi.server.UID class. As almost every class representing an xml element in the Java API needs a unique ID (e.g. for "linking" the elements. see above example with observer), this class generates the IDs. (As it's not sure if the IDs should be more self explaining in the future, the class de.lehmannet.om.util.UIDGenerator itself should always be used over its interface de.lehmannet.om.util.IIDGenerator).
The classes
de.lehmannet.om.util.ConfigLoader and  de.lehmannet.om.util.SchemaLoader are needed for reading/parsing a XML File containing a valid XML Standard description (for more please see Reading XML Schema Files).
For the implementation of the abstract elements <result> and <target>, extension packages are defined. Currently the only extension is the DeepSky Extension (de.lehmannet.om.extension.deepSky). The extensions are specialized classes for a specific observation targets (like DeepSky Objects) and observations results. (Simple Example: A observation target inside the solar system, has to have other attributes as a observation target that is a DeepSky Object (e.g. a Galaxy). So are the observation results))


 4.0 Writing XML Schema Files

This paragraph should show you in an short example how to create a valid observation and how to serialize it.
Creating an observation is pretty straight forward. You instantiate a class of
de.lehmannet.om.Observation and meanly thats it. OK, for instantiating you need some more instances of other API classes. Lets look at this:
Say you observed M45 (Messier 45, The Pleiads). As they're an open star cluster, which is a DeepSky object, you need some class of the DeepSky extension.
de.lehmannet.om.extension.deepSky.DeepSkyTargetOC is specialized for open clusters so:

ITarget myObservedTarget = new DeepSkyTargetOC("M45", "Messier Catalogue");

thats about all. You can set some more parameters, but we skip this for now. Next we need an de.lehmannet.om.Observer object. This can easily be done like this:

IObserver thatsMe = new Observer("John", "Doe");

Next, we need a observation site. Somewhere the observation took place. So here we go:

ISite myObservingSite = new Site("Wehrheim, Germany",
                                 new Angle(8.567, Angle.DEGREE),
                                 new Angle(50.3, Angle.DEGREE),
                                 2
                                 );


This is it. The first parameter is the name of the site, the second one is the longitude (where positive values are east of Greenwich, England). The third parameter this the latitude, where positive value indicate north of the equator. The last parameter this the timezone. Now you also used one of the most famous helper classes of the API:
de.lehmannet.om.Angle The class is always used to represent some sort of an angle, whatever unit it may have. Position angle, latitude, longitude... everything that can be packed into an angle is represented by de.lehmannet.om.Angle
The next thing we need to represent is the telescope we made the observation with. Therefore we use
de.lehmannet.om.Scope.

IScope myTelescope = new Scope("Starfinder 10",
                               254
                               1140
                               );


The first parameter is a name for the telescope (can be vendor name and model or the name you personally call the scope....doesn't matter here). Second is the aperture
given in mm, and third is the focal length also in mm.
Next, if we used a telescope, we also used a eyepiece. So lets build up an eyepiece object with:
de.lehmannet.om.Eyepiece

IEyepiece uncleAlsBestOne = new Eyepiece("Nagler",
                                         31
                                         );


This is the easiest and cheapest way to get a 31 Nagler. :-)
Next, is the object
de.lehmannet.om.Session. A session describes an observing night or a group of observations, that have been all made at the same observation session/night. This is not necessary here, but we create one nevertheless.

ISession theNight = new Session(new java.util.Date(2004, 01, 01, 22, 00),
                                new java.util.Date(2004, 01, 01, 23, 30));

So the observing night, took place at the 1 of January 2004, and started at 22:00 local time and lasted until 23:30 local time.
Last object we need is a
de.lehmannet.om.IFinding instance. As we observed a DeepSky target, the class de.lehmannet.om.extension.deepSky.DeepSkyFinding is specialized for this kind of observation results.

IFinding myFindings = new DeepSkyFinding("Wow! There're more than 7 stars!",
                                         1
                                         );


The first parameter is a description of what the observer saw. This can be only a short "Wow!" up to the whole life story of the observer that takes pages.
The second parameter is special for
de.lehmannet.om.extension.deepSky.DeepSkyFinding. It describes something like the "quality" the object could be seen. 1 stands for  "Simple conspicuous object in the eyepiece". (For a complete list of possible rates, please see the JavaDocs of de.lehmannet.om.extension.deepSky.DeepSkyFinding at our online JavaDoc section.

As we got everything we need, lets put it all together to one observation:

IObservation observationOfM45 = new Observation(new java.util.Date(2004, 01, 01, 22, 15),
                                                new java.util.Date(2004, 01, 01, 22, 30),
                                                5.0f,
                                                2,
                                                150.0f,
                                               
myObservedTarget,
                                               
thatsMe,
                                               
myObservingSite,
                                               
myTelescope,
                                               
uncleAlsBestOne,
                                               
theNight,
                                               
myFindings
                                                );

That's it. We got our first observation! Something more on the parameters: The first one is the start date of the observation and the second one is the end date. Please mind: these dates refer to the times, the observer looked on the given target. The start and end time of the whole observing night, are captured in the session object! Also please mind, that the observations start and end time, have to be inside the sessions start and end time (Yes, the API is restrictive..:-) )
The third value is the magnitude of the faintest star that could be seen with the unaided eye during the observation. Fourth is the seeing (1=best, 5 worst)  (see JavaDocs for more), fifth is the magnification (can be calculated by telescopes focal length and eyepiece length). All other parameters you should know now.

So now we got everything as java objects but how to persist them as a valid xml?
Therefore look at a sample XML. The xml standard always starts with a
<fgca:observations> element. This is represented in the Java API by the class de.lehmannet.om.Observations (don't mess this with de.lehmannet.om.Observation the first one is a container, while the second is a "real" observation). So lets get the observations container:

Observations observationsContainer = new Observations();
try {
  observationsContainer.addObservation(
observationOfM45);
} catch(SchemaException se)
{
    // Thrown e.g. if passed object as NULL
}

After we got the container we can use its serializeAsXml(java.io.File) method, to write the observation in a file:
       
try {
  observations.serializeAsXml(new java.io.File(thePathToTheXmlFile));
} catch(SchemaException se) {
    // Something went wrong. Maybe IO error? See exceptions message...
}


That's it. Now you've seen how to write valid xml files that fit the xml standard!



 5.0 Reading XML Schema Files

This paragraph should explain how you could use the Java API for parsing the xml standard. As a result of the parsing of a xml standard file you should get a instance of de.lehmannet.om.Observations  that contains the xml data as Java objects.
Also this paragraph will explain how the Java API parses the xml data, and what does it do in the background.

Loading a xml file

The parsing of a xml file starts with
de.lehmannet.om.util.SchemaLoader.  This class offers two mean methods: load(java.io.File) and load(org.w3c.dom.Document). Both methods do the same only the parameter is different.
If you got a
java.io.File object that represents a xml file containing valid xml standard data, all you got to do is the following to parse the file:

File myXmlFile = new File("/home/john/observations.xml");

SchemaLoader parser = null;
try {
    parser = new SchemaLoader();
    Observations observationsContainer =
parser.load(myXmlFile);
} catch(FGCAException fgca) {
    // Problem while parsing the xml file
}

The returned object is a instance of
de.lehmannet.om.Observations, that you already know from the above chapter.
The object contains all data of the xml file, represented as Java Objects.
As a user of the JavaAPI this is all you got to know about how xml files can be transformed to Java API objects.

For expanding and understanding the JavaAPI, you should know what's behind the scenes of
de.lehmannet.om.util.SchemaLoader. What is class does on a first glance is pretty simple. It scans the xml file for the known container elements and if it finds a container it parses all the contained elements. Almost all Java API elements have a constructor like this Scope(org.w3c.dom.Node). It enables the corresponding class to create itself out of a org.w3c.dom.Node element, where the given node is always the root of the corresponding element, e.g. <scope>.

This is all more or less straight forward. The trouble begins with the xml schema elements
<result> (as subelement of <observation>) and <target>.
These two elements are defined abstract by the standard. Means their real implementation are represented by the optional extensions. Currently we offer only the DeepSky extension, that implements the abstract classes, but hopefully we can offer more extensions in the future (e.g. SolarSystem, VariableStars....).
But how do we know while parsing a xml file, which implementation of e.g. a target we've found? This we can tell by a xml element attribute called
xsi:type
that must be set at all
<result> and <target> subclasses (or subelements). For example the open cluster deep sky targets (which are represented in the Java API as de.lehmannet.om.extension.deepSky.DeepSkyTargetOC) have the xsi:type value fgca:deepSkyOC.
So if the parser finds a 
<result> or a <target> element, it searches for the xsi:type attribute as well. But knowing the xsi:type is only the half way to get the corresponding Java API class for the element. Therefore a mapping is defined that links a xsi:type with a java class. This mapping must be stored in a file called:
SCHEMATYPE which has to be located in the META-INF path of the jar that contains the Java API extension classes.
If you open for example the ext_DeepSky.zip file, you see the
SCHEMATYPE file, under META-INF. This file defines the mapping between a xsi:type value that can be found at the xml elements and the corresponding Java API class.
The parser resolves the xsi:type with the help of the class
de.lehmannet.om.util.ConfigLoader , which tells him the java class which he has to load via javas reflection mechanism.
With the above mentioned procedure, the Java API is flexible enough to handle all future xml standard extensions which may come. Another main advantage of this procedure is that it's easy to handle by the user. All he needs to do is putting the extension jar into his java classpath. The 
de.lehmannet.om.util.SchemaLoader (better the de.lehmannet.om.util.ConfigLoader class) will find it there.

The format of the SCHEMATYPE file

To ensure that the SCHEMATYPE file can be loaded, it has to have a simple format, that will be explained here.
An entry for a single mapping (xsi:type to java class) has to have two lines.

Example:
DS_TargetDN_XSI_Relation_Class: de.lehmannet.om.extension.deepSky.DeepSkyTargetDN
DS_TargetDN_XSI_Relation_Type: fgca:deepSkyDN

The first part of a line is the key which ends with a colon (:). The last part is the value.
The key itself is build up of two parts. The identifier and the type. The type can only have two values: XSI_Relation_Class and XSI_Relation_Type.
In the example above
DS_TargetDN_ is the identifier followed by its type XSI_Relation_Class, the key is separated at the end with a colon.

For a valid entry both types (
XSI_Relation_Class and XSI_Relation_Type) have to appear in the file, both with the same key identifier. The identifier can be chosen by the developer of the extension. The value of the type XSI_Relation_Class must contain the java class, while the value of the type XSI_Relation_Type must be the xsi:type.

Please mind, that the file must be placed inside the
META-INF folder of the jar that contains the extensions classes.