/*
* ALMA - Atacama Large Millimiter Array
* (c) European Southern Observatory, 2002
* Copyright by ESO (in the framework of the ALMA collaboration),
* All rights reserved
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
package alma.acs.exceptions;
import java.util.Properties;
import alma.ACSErr.Completion;
import alma.ACSErr.ErrorTrace;
import alma.acs.util.UTCUtility;
/**
* Java class that facilitates working with the interoperable {@link Completion} that's
* generated from CORBA IDL.
* <p>
* A <code>Completion</code> transmits the result of a remote method invocation
* to the client. It contains an error type and code as defined in the ACS framework,
* as well as an optional <code>ErrorTrace</code>.
* Note that for asynchronous calls, it is not possible to transmit thrown exceptions
* to the client directly; here the <code>Completion</code> fills the gap.
* <p>
* Similar to <code>AcsJException</code>, <code>AcsJCompletion</code> converts CORBA <code>struct</code>
* data into its nicer Java representation upon construction. When an <code>AcsJCompletion</code>
* must be transported further on over CORBA, the method {@link #toCorbaCompletion} will create
* a corresponding <code>Completion</code> with correct <code>ErrorTrace</code> structs attached.
* <p>
* For any combination of error type/code, the ACS framework generates subclasses
* of <code>AcsJCompletion</code>, which ensure usage of the correct type and code.
* <ul>
* <li> To create a completion object from scratch, use the appropriate subclass;
* <li> To create a completion from the base type <code>AcsJException</code>,
* which may be useful inside a catch block, use {@link AcsJException#toAcsJCompletion()}.
* <li> To convert an existing CORBA <code>Completion</code> to an <code>AcsJCompletion</code>,
* use the static method {@link #fromCorbaCompletion}.
* </ul>
*
* @author hsommer
* created Jan 20, 2004 3:11:34 PM
*/
public class AcsJCompletion
{
private int m_type;
private int m_code;
private long m_timestamp;
/** optional exception; if present, type and code of completion and exception must be the same */
private AcsJException m_jex;
// additional name-value pairs
protected Properties m_properties;
protected AcsJCompletion()
{
}
/**
* To be called from ctors of generated subclasses that can't have exceptions attached.
* @param type
* @param code
*/
protected AcsJCompletion(int type, int code)
{
init(type, code);
}
/**
* To be called from ctors of generated subclasses that have exceptions attached.
*
* @param acsJEx
*/
protected AcsJCompletion(AcsJException acsJEx)
{
init(acsJEx);
}
protected void init(int type, int code)
{
m_type = type;
m_code = code;
m_timestamp = System.currentTimeMillis();
m_jex = null;
}
/**
* Initializes this <code>AcsJCompletion</code> object with the data of
* an <code>AcsJException</code> (chain).
* <p>
* Type, code, and timestamp are taken from <code>acsJEx</code>.
*
* @param acsJEx
* @throws NullPointerException if <code>acsJEx</code> is <code>null</code>.
*/
protected void init(AcsJException acsJEx)
{
if (acsJEx != null)
{
m_type = acsJEx.getErrorType();
m_code = acsJEx.getErrorCode();
m_timestamp = acsJEx.getTimestampMillis();
m_jex = acsJEx;
}
else
{
throw new NullPointerException("argument 'acsJEx' must not be null!");
}
}
/**
* Returns the completion type.
*/
public int getType() {
return m_type;
}
/**
* Returns the completion code.
*/
public int getCode() {
return m_code;
}
/**
* Returns the timeStamp.
*/
public long getTimeStamp() {
return m_timestamp;
}
/**
* True if this completion represents an error condition.
* The error information can be obtained from {@link #getAcsJException}.
*/
public boolean isError()
{
return (m_jex != null );
}
/**
*
* @return an exception (chain) corresponding to the <code>ErrorTrace</code>,
* or <code>null</code> if the completion does not correspond to an exception.
* @see #isError
*/
public AcsJException getAcsJException()
{
return m_jex;
}
/**
* Creates a CORBA style completion object from this Java style completion.
* If present, the attached exceptions are converted to <code>ErrorTrace</code>s.
* <p>
* This method should be used when a Java implementation (of a component etc)
* has to send an <code>AcsJCompletion</code> over Corba.
* @see #fromCorbaCompletion
*/
public Completion toCorbaCompletion()
{
Completion corbaCompl = new Completion();
corbaCompl.type = getType();
corbaCompl.code = getCode();
corbaCompl.timeStamp = UTCUtility.utcJavaToOmg(getTimeStamp());
if (isError())
{
corbaCompl.previousError = new ErrorTrace[1];
corbaCompl.previousError[0] = getAcsJException().getErrorTrace();
}
else
{
// create empty array so that CORBA can transport the struct w/o NPE
corbaCompl.previousError = new ErrorTrace[0];
}
return corbaCompl;
}
/**
* Factory method to create an <code>AcsJCompletion</code> from an existing CORBA completion.
* Note that the new <code>AcsJCompletion</code> is a direct translation, not a wrapper.
* <p>
* If <code>corbaCompletion</code> has error information attached,
* this will be converted, and type/code of the top-exception will
* have precedence over type/code stored redundantly in <code>corbaCompletion</code>.
* <p>
* To be used on the client side of a remote call.
* @param completion
*/
public static AcsJCompletion fromCorbaCompletion(Completion corbaCompletion)
{
if (corbaCompletion == null)
throw new NullPointerException("argument 'completion' must not be null!");
AcsJCompletion jcompletion = new AcsJCompletion();
// does completion have error info attached? If so, convert from CORBA structs to Java exc.
if (corbaCompletion.previousError != null && corbaCompletion.previousError.length > 0)
{
AcsJException jex = null;
ErrorTrace et = corbaCompletion.previousError[0];
Throwable thr = CorbaExceptionConverter.recursiveGetThrowable(et);
if (! (thr instanceof AcsJException))
{
// just in case... should never happen, as CorbaExceptionConverter.recursiveGetThrowable
// already substitutes non-AcsJ exceptions with DefaultAcsJException...
jex = new DefaultAcsJException(et);
}
else
{
jex = (AcsJException) thr;
}
jcompletion.init(jex);
}
else
{
jcompletion.init(corbaCompletion.type, corbaCompletion.code);
}
return jcompletion;
}
/**
* Allows extra information to be attached to the completion, but only if this completion represents an error.
* @return the previous value of the specified key in this property
* list, or <code>null</code> if it did not have one.
* @throws IllegalStateException if this completion has no exceptions attached (i.e. it is an "ok-completion").
* TODO: modify ACS error system to allow properties also for ok-completions.
*/
public Object setProperty(String key, String value) {
if (isError()) {
return getAcsJException().setProperty(key, value);
}
else {
throw new IllegalStateException("Failed to set property '" + key + "' because properties are not supported for ok-completion classes.");
}
}
/**
* @see #setProperty
*/
public String getProperty(String key) {
if (isError()) {
return getAcsJException().getProperty(key);
}
else {
throw new IllegalStateException("Failed to get property '" + key + "' because properties are not supported for ok-completion classes.");
}
}
}