Table of Contents
abstract
Classesfinal
Classespublic
Classesfinal
Fields and static
Constant Variables
static
Fieldstransient
Fieldsabstract
Methodsfinal
Methodsnative
Methodsstatic
Methodssynchronized
Methods
Development tools for the
Java programming language should support automatic recompilation as necessary
whenever source code is available. Particular implementations may also
store the source and binary of types in a versioning database and
implement a ClassLoader
that uses integrity mechanisms of the
database to prevent linkage errors by providing binary-compatible
versions of types to clients.
Developers of packages and classes that are to be widely distributed face a different set of problems. In the Internet, which is our favorite example of a widely distributed system, it is often impractical or impossible to automatically recompile the pre-existing binaries that directly or indirectly depend on a type that is to be changed. Instead, this specification defines a set of changes that developers are permitted to make to a package or to a class or interface type while preserving (not breaking) compatibility with pre-existing binaries.
Within the framework of Release-to-Release Binary Compatibility in SOM (Forman, Conner, Danforth, and Raper, Proceedings of OOPSLA '95), Java programming language binaries are binary compatible under all relevant transformations that the authors identify (with some caveats with respect to the addition of instance variables). Using their scheme, here is a list of some important binary compatible changes that the Java programming language supports:
Reimplementing existing methods, constructors, and initializers to improve performance.
Changing methods or constructors to return values on inputs for which they previously either threw exceptions that normally should not occur or failed by going into an infinite loop or causing a deadlock.
Adding new fields, methods, or constructors to an existing class or interface.
Deleting private
fields, methods, or constructors of a
class.
When an entire package is updated, deleting package access fields, methods, or constructors of classes and interfaces in the package.
Reordering the fields, methods, or constructors in an existing type declaration.
Reordering the list of direct superinterfaces of a class or interface.
Inserting new class or interface types in the type hierarchy.
This chapter specifies minimum standards for binary compatibility guaranteed by all implementations. The Java programming language guarantees compatibility when binaries of classes and interfaces are mixed that are not known to be from compatible sources, but whose sources have been modified in the compatible ways described here. Note that we are discussing compatibility between releases of an application. A discussion of compatibility among releases of the Java SE platform is beyond the scope of this chapter.
We encourage development systems to provide facilities that alert developers to the impact of changes on pre-existing binaries that cannot be recompiled.
This chapter first specifies some properties that any binary format for the Java programming language must have (§13.1). It next defines binary compatibility, explaining what it is and what it is not (§13.2). It finally enumerates a large set of possible changes to packages (§13.3), classes (§13.4), and interfaces (§13.5), specifying which of these changes are guaranteed to preserve binary compatibility and which are not.
Occasionally, references of the form: (JVMS §x.y) are used to indicate concepts from The Java Virtual Machine Specification, Java SE 8 Edition.
Programs
must be compiled either into the class
file format specified by
The Java Virtual Machine Specification, Java SE 8 Edition, or into a
representation that can be mapped into that format
by a class loader written in the Java programming language.
The resulting class
file must have certain properties. A number of
these properties are specifically chosen to support source code
transformations that preserve binary compatibility. The required
properties are:
The class or interface must be named by its binary name, which must meet the following constraints:
The binary name of a top level type (§7.6) is its canonical name (§6.7).
The binary name of a member type (§8.5,
§9.5) consists of the binary
name of
its immediately enclosing type, followed by $
,
followed by the simple name of the member.
The binary name of a local class
(§14.3) consists of the
binary name of
its immediately enclosing type, followed by $
,
followed by a non-empty sequence of digits, followed by the
simple name of the local class.
The binary name of an anonymous class
(§15.9.5) consists of
the binary name
of its immediately enclosing type, followed by $
,
followed by a non-empty sequence of digits.
The binary name of a type variable declared by a generic
class or interface (§8.1.2,
§9.1.2) is
the binary name of its
immediately enclosing type, followed by $
, followed
by the simple name of the type variable.
The binary name of a type variable declared by a generic
method (§8.4.4) is the binary name
of
the type declaring the method, followed by $
,
followed by the descriptor of the method (JVMS §4.3.3),
followed by $
, followed by the simple name of the
type variable.
The binary name of a type variable declared by a generic
constructor (§8.8.4) is the
binary name
of the type declaring the constructor, followed by $
,
followed by the descriptor of the constructor (JVMS §4.3.3),
followed by $
, followed by the simple name of the
type variable.
A reference to another class or interface type must be symbolic, using the binary name of the type.
A reference to a field that is a constant variable (§4.12.4) must be resolved at compile time to the value V denoted by the constant variable's initializer.
If such a field is static
, then no reference to the field
should be present in the code in a binary file, including the
class or interface which declared the field. Such a field must
always appear to have been initialized
(§12.4.2); the default
initial value for
the field (if different than V) must never be observed.
If such a field is non-static
, then no reference to the field
should be present in the code in a binary file, except in the
class containing the field. (It will be a class rather than an
interface, since an interface has only static
fields.) The
class should have code to set the field's value to V during
instance creation (§12.5).
Given a legal expression denoting a field access in a class C,
referencing a field named f
that is not a
constant variable and is declared in a (possibly distinct) class
or interface D, we define the qualifying type of the
field reference as follows:
If the expression is referenced by a simple name, then if
f
is a member of the current class or interface, C, then
let T be C. Otherwise, let T be the innermost
lexically enclosing type declaration of which f
is a
member. In either case, T is the qualifying type of the
reference.
If the reference is of the form TypeName.
f
, where
TypeName denotes a class or interface, then
the class or
interface denoted by TypeName is the
qualifying type of
the reference.
If the expression is of the form ExpressionName.
f
or
Primary.
f
, then:
If the compile-time type of ExpressionName or
Primary is an intersection type
V1 &
... &
Vn (§4.9),
then the qualifying
type of the reference is V1.
Otherwise, the compile-time type of ExpressionName or Primary is the qualifying type of the reference.
If the expression is of the form super
.
f
, then the
superclass of C is the qualifying type of the
reference.
If the expression is of the form
TypeName.
super
.
f
, then the superclass of the
class denoted by TypeName is the qualifying
type of the
reference.
The reference to f
must be compiled into a symbolic reference
to the erasure (§4.6)
of the qualifying
type of the reference, plus the simple name of the field,
f
. The reference must also include a symbolic reference to the
erasure of the declared type of the field so that the verifier
can check that the type is as expected.
Given a method invocation expression or a method reference
expression in a class or interface C, referencing a method
named m
declared (or implicitly declared
(§9.2))
in a (possibly distinct) class or
interface D, we define the qualifying type of the
method invocation as follows:
If D is Object
then the
qualifying type of the
expression is Object
.
If the method is referenced by a simple name, then if
m
is a member of the current class or
interface C,
let T be C;
otherwise, let T be the innermost
lexically enclosing type declaration of which m
is a
member. In either case, T is the qualifying
type of
the method invocation.
If the expression is of the form TypeName.
m
or
ReferenceType::
m
, then
the type denoted by
TypeName or ReferenceType is the qualifying
type of
the method invocation.
If the expression is of the form
ExpressionName.
m
or Primary.
m
or
ExpressionName::
m
or
Primary::
m
, then:
If the compile-time type of ExpressionName
or
Primary is an
intersection type V1
&
... &
Vn (§4.9),
then the
qualifying type of the method invocation is
V1.
Otherwise, the compile-time type of ExpressionName or Primary is the qualifying type of the method invocation.
If the expression is of the form super
.
m
or
super
::
m
, then the superclass of C is
the qualifying type of the method invocation.
If the expression is of the form
TypeName.
super
.
m
or
TypeName.
super
::
m
, then if
TypeName denotes a class X, the superclass of X is
the qualifying type of the method invocation; if
TypeName denotes an interface
X, X is the
qualifying type of the method invocation.
A reference to a method must be resolved at compile time to a symbolic reference to the erasure (§4.6) of the qualifying type of the invocation, plus the erasure of the signature (§8.4.2) of the method. The signature of a method must include all of the following as determined by §15.12.3:
A reference to a method must also include either a symbolic
reference to the erasure of the return type of the denoted
method or an indication that the denoted method is declared
void
and does not return a value.
Given a class instance creation expression
(§15.9) or an explicit
constructor
invocation statement (§<IP_ADDRESS>)
or a method
reference expression of the form ClassType
::
new
(§15.13) in a
class or interface C referencing a constructor m
declared in
a (possibly distinct) class or interface D, we define the
qualifying type of the constructor invocation as follows:
If the expression is of the form
new
D(...)
or
ExpressionName.
new
D(...)
or
Primary.
new
D(...)
or
D ::
new
,
then the qualifying type of the invocation is D.
If the expression is of the form
new
D(...){...}
or
ExpressionName.
new
D(...){...}
or
Primary.
new
D(...){...}
,
then the qualifying type of the expression is the
compile-time type of the expression.
If the expression is of the form
super
(...)
or
ExpressionName.
super
(...)
or
Primary.
super
(...)
, then the
qualifying type of the expression is the direct superclass
of C.
If the expression is of the form
this
(...)
, then the
qualifying type of
the expression is C.
A reference to a constructor must be resolved at compile time to a symbolic reference to the erasure (§4.6) of the qualifying type of the invocation, plus the signature of the constructor (§8.8.2). The signature of a constructor must include both:
A binary representation for a class or interface must also contain all of the following:
If it is a class and is not Object
, then a symbolic reference
to the erasure of the direct superclass of this class.
A symbolic reference to the erasure of each direct superinterface, if any.
A specification of each field declared in the class or interface, given as the simple name of the field and a symbolic reference to the erasure of the type of the field.
If it is a class, then the erased signature of each constructor, as described above.
For each method declared in the class or interface (excluding, for an interface, its implicitly declared methods (§9.2)), its erased signature and return type, as described above.
Every type must contain sufficient information to recover its canonical name (§6.7).
Every member type must have sufficient information to recover its source level access modifier.
Every nested class and nested interface must have a symbolic reference to its immediately enclosing class (§8.1.3).
Every class must contain symbolic references to all of its member types (§8.5), and to all local and anonymous classes that appear in its methods, constructors, static initializers, instance initializers, and field initializers.
Every interface must contain symbolic references to all of its member types (§9.5), and to all local and anonymous classes that appear in its default methods and field initializers.
A construct emitted by a Java compiler must be marked as synthetic if it does not correspond to a construct declared explicitly or implicitly in source code, unless the emitted construct is a class initialization method (JVMS §2.9).
A construct emitted by a Java compiler must be marked as mandated if it corresponds to a formal parameter declared implicitly in source code (§8.8.1, §8.8.9, §8.9.3, §<IP_ADDRESS>).
The following formal parameters are declared implicitly in source code:
The first formal parameter of a constructor of a
non-private
inner member class (§8.8.1,
§8.8.9).
The first formal parameter of an anonymous constructor of an anonymous class whose superclass is inner or local (not in a static context) (§<IP_ADDRESS>).
The formal parameter
name
of the valueOf
method
which is implicitly declared in an enum type
(§8.9.3).
For reference, the following constructs are declared
implicitly in source code, but are not marked as mandated because only
formal parameters can be so marked in a class
file (JVMS
§4.7.22):
The following sections discuss
changes that may be made to class and interface type declarations
without breaking compatibility with pre-existing binaries. Under the
translation requirements given above, the Java Virtual Machine and its class
file
format support these changes. Any other valid binary format, such as a
compressed or encrypted representation that is mapped back into
class
files by a class loader under the above requirements, will
necessarily support these changes as well.
A change to a type is binary compatible with (equivalently, does not break binary compatibility with) pre-existing binaries if pre-existing binaries that previously linked without error will continue to link without error.
Binaries are compiled to rely on the accessible members and constructors of other classes and interfaces. To preserve binary compatibility, a class or interface should treat its accessible members and constructors, their existence and behavior, as a contract with its users.
The Java programming language is designed to prevent additions to contracts and accidental name collisions from breaking binary compatibility. Specifically, addition of more methods overloading a particular method name does not break compatibility with pre-existing binaries. The method signature that the pre-existing binary will use for method lookup is chosen by the overload resolution algorithm at compile time (§15.12.2).
If the Java programming language had been designed so that the particular method to be executed was chosen at run time, then such an ambiguity might be detected at run time. Such a rule would imply that adding an additional overloaded method so as to make ambiguity possible at a call site could break compatibility with an unknown number of pre-existing binaries. See §13.4.23 for more discussion.
Binary compatibility is not the same as source compatibility. In particular, the example in §13.4.6 shows that a set of compatible binaries can be produced from sources that will not compile all together. This example is typical: a new declaration is added, changing the meaning of a name in an unchanged part of the source code, while the pre-existing binary for that unchanged part of the source code retains the fully-qualified, previous meaning of the name. Producing a consistent set of source code requires providing a qualified name or field access expression corresponding to the previous meaning.
A new top level class or interface type may be added to a package without breaking compatibility with pre-existing binaries, provided the new type does not reuse a name previously given to an unrelated type.
If a new type reuses a name previously given to an unrelated type, then a conflict may result, since binaries for both types could not be loaded by the same class loader.
Changes in top level class and
interface types that are not public
and that are not a superclass or
superinterface, respectively, of a public
type, affect only types
within the package in which they are declared. Such types may be
deleted or otherwise changed, even if incompatibilities are otherwise
described here, provided that the affected binaries of that package
are updated together.
This section describes the effects of changes to the declaration of a class and its members and constructors on pre-existing binaries.
If a class that was not
declared abstract
is changed to be declared abstract
, then
pre-existing binaries that attempt to create new instances of that
class will throw either an InstantiationError
at link time, or (if a
reflective method is used) an InstantiationException
at run time;
such a change is therefore not recommended for widely distributed
classes.
Changing a class that is
declared abstract
to no longer be declared abstract
does not break
compatibility with pre-existing binaries.
If a class that was not
declared final
is changed to be declared final
,
then a VerifyError
is
thrown if a binary of a pre-existing subclass of this class is loaded,
because final
classes can have no subclasses; such a change is not
recommended for widely distributed classes.
Changing a class that is
declared final
to no longer be declared final
does not break
compatibility with pre-existing binaries.
Changing a class that is not
declared public
to be declared public
does not
break compatibility
with pre-existing binaries.
If a class that was declared
public
is changed to not be declared public
,
then an IllegalAccessError
is
thrown if a pre-existing binary is linked that needs but no longer has
access to the class type; such a change is not recommended for widely
distributed classes.
A ClassCircularityError
is
thrown at load time if a class would be a superclass of
itself. Changes to the class hierarchy that could result in such a
circularity when newly compiled binaries are loaded with pre-existing
binaries are not recommended for widely distributed classes.
Changing the direct superclass or the set of direct superinterfaces of a class type will not break compatibility with pre-existing binaries, provided that the total set of superclasses or superinterfaces, respectively, of the class type loses no members.
If a change to the direct superclass or the set of direct superinterfaces results in any class or interface no longer being a superclass or superinterface, respectively, then linkage errors may result if pre-existing binaries are loaded with the binary of the modified class. Such changes are not recommended for widely distributed classes.
Example 13.4.4-1. Changing A Superclass
Suppose that the following test program:
class Hyper { char h = 'h'; } class Super extends Hyper { char s = 's'; } class Test extends Super { public static void printH(Hyper h) { System.out.println(h.h); } public static void main(String[] args) { printH(new Super()); } }
is compiled and executed, producing the output:
h
Suppose that a new version of
class Super
is then compiled:
class Super { char s = 's'; }
This version of class Super
is
not a subclass of Hyper
. If we then run the
existing binaries of Hyper
and Test
with the new version
of Super
, then a VerifyError
is
thrown at link time. The verifier objects because the result
of new Super()
cannot be passed as an argument in
place of a formal parameter of type Hyper
,
because Super
is not a subclass
of Hyper
.
It is instructive to consider what might happen without the verification step: the program might run and print:
s
This demonstrates that without the verifier, the Java type system could be defeated by linking inconsistent binary files, even though each was produced by a correct Java compiler.
The lesson is that an implementation that lacks a verifier or fails to use it will not maintain type safety and is, therefore, not a valid implementation.
The requirement that alternatives in a multi-catch
clause (§14.20)
not be subclasses or superclasses
of each other is only a source restriction. Assuming the following
client code is legal:
try { throwAorB(); } catch(ExceptionA | ExceptionB e) { ... }
where ExceptionA
and ExceptionB
do not have a subclass/superclass
relationship when the client is compiled, it is binary compatible with
respect to the client for ExceptionA
and ExceptionB
to have such a relationship when the
client is executed.
This is analogous to other situations where a class transformation that is binary compatible for a client might not be source compatible for the same client.
Adding or removing a type parameter of a class does not, in itself, have any implications for binary compatibility.
If such a type parameter is used in the type of a field or method, that may have the normal implications of changing the aforementioned type.
Renaming a type parameter of a class has no effect with respect to pre-existing binaries.
Changing the first bound of a type parameter of a class may change the erasure (§4.6) of any member that uses that type parameter in its own type, and this may affect binary compatibility. The change of such a bound is analogous to the change of the first bound of a type parameter of a method or constructor (§13.4.13).
Changing any other bound has no effect on binary compatibility.
No incompatibility with
pre-existing binaries is caused by adding an instance (respectively
static
) member that has the same name and accessibility (for
fields), or same name and accessibility and signature and return type
(for methods), as an instance (respectively static
) member of a
superclass or subclass. No error occurs even if the set of classes
being linked would encounter a compile-time error.
Deleting a class member or
constructor that is not declared private
may cause a linkage error
if the member or constructor is used by a pre-existing binary.
Example 13.4.6-1. Changing A Class Body
class Hyper { void hello() { System.out.println("hello from Hyper"); } } class Super extends Hyper { void hello() { System.out.println("hello from Super"); } } class Test { public static void main(String[] args) { new Super().hello(); } }
This program produces the output:
hello from Super
Suppose that a new version of
class Super
is produced:
class Super extends Hyper {}
Then, recompiling Super
and
executing this new binary with the original binaries
for Test
and Hyper
produces the
output:
hello from Hyper
as expected.
The super
keyword can be
used to access a method declared in a superclass, bypassing any
methods declared in the current class. The expression
super
.
Identifier is resolved, at compile time, to a method m
in the superclass S. If the method m
is an
instance method, then
the method which is invoked at run time is the method with the same
signature as m
that is a member of the direct superclass of the
class containing the expression involving super
.
Example 13.4.6-2. Changing A Superclass
class Hyper { void hello() { System.out.println("hello from Hyper"); } } class Super extends Hyper { } class Test extends Super { public static void main(String[] args) { new Test().hello(); } void hello() { super.hello(); } }
This program produces the output:
hello from Hyper
Suppose that a new version of
class Super
is produced:
class Super extends Hyper { void hello() { System.out.println("hello from Super"); } }
Then, if Super
and Hyper
are recompiled but
not Test
, then running the new binaries with the
existing binary of Test
produces the output:
hello from Super
as you might expect.
Changing the declared access
of a member or constructor to permit less access may break
compatibility with pre-existing binaries, causing a linkage error to
be thrown when these binaries are resolved. Less access is permitted
if the access modifier is changed from package access to private
access; from protected
access to package or private
access; or
from public
access to protected
, package, or
private
access. Changing a member or constructor to permit less access is
therefore not recommended for widely distributed classes.
Perhaps surprisingly, the binary format is defined so that changing a member or constructor to be more accessible does not cause a linkage error when a subclass (already) defines a method to have less access.
Example 13.4.7-1. Changing Accessibility
If the package points
defines the
class Point
:
package points; public class Point { public int x, y; protected void print() { System.out.println("(" + x + "," + y + ")"); } }
used by the program:
class Test extends points.Point { public static void main(String[] args) { Test t = new Test(); t.print(); } protected void print() { System.out.println("Test"); } }
then these classes compile
and Test
executes to produce the output:
Test
If the method print
in
class Point
is changed to be public
,
and then
only the Point
class is recompiled, and then
executed with the previously existing binary
for Test
, then no linkage error occurs. This
happens even though it is improper, at compile time, for a public
method to be overridden by a protected
method (as shown by the fact
that the class Test
could not be recompiled using
this new Point
class
unless print
in Test
were
changed to be public
.)
Allowing superclasses to
change protected
methods to be public
without
breaking binaries of
pre-existing subclasses helps make binaries less fragile. The
alternative, where such a change would cause a linkage error, would
create additional binary incompatibilities.
Widely distributed programs should not expose any fields to their clients. Apart from the binary compatibility issues discussed below, this is generally good software engineering practice. Adding a field to a class may break compatibility with pre-existing binaries that are not recompiled.
Assume a reference to a
field f
with qualifying type T. Assume further
that f
is in fact
an instance (respectively static
) field declared in a superclass of
T, S, and that the type of f
is X.
If a new field of type X
with the same name as f
is added to a subclass of S that is a
superclass of T or T itself, then a linkage error
may occur. Such
a linkage error will occur only if, in addition to the above, either
one of the following is true:
In particular, no linkage error will occur in the case where a class could no longer be recompiled because a field access previously referenced a field of a superclass with an incompatible type. The previously compiled class with such a reference will continue to reference the field declared in a superclass.
Example 13.4.8-1. Adding A Field Declaration
class Hyper { String h = "hyper"; } class Super extends Hyper { String s = "super"; } class Test { public static void main(String[] args) { System.out.println(new Super().h); } }
This program produces the output:
hyper
Suppose a new version of
class Super
is produced:
class Super extends Hyper { String s = "super"; int h = 0; }
Then, recompiling Hyper
and Super
, and executing the resulting new binaries
with the old binary of Test
produces the
output:
hyper
The field h
of Hyper
is output by the original binary
of Test
. While this may seem surprising at first,
it serves to reduce the number of incompatibilities that occur at run
time. (In an ideal world, all source files that needed recompilation
would be recompiled whenever any one of them changed, eliminating such
surprises. But such a mass recompilation is often impractical or
impossible, especially in the Internet. And, as was previously noted,
such recompilation would sometimes require further changes to the
source code.)
As another example, if the program:
class Hyper { String h = "Hyper"; } class Super extends Hyper { } class Test extends Super { public static void main(String[] args) { String s = new Test().h; System.out.println(s); } }
is compiled and executed, it produces the output:
Hyper
Suppose that a new version of
class Super
is then compiled:
class Super extends Hyper { char h = 'h'; }
If the resulting binary is used with the existing
binaries for Hyper
and Test
,
then the output is still:
Hyper
even though compiling the source for these binaries:
class Hyper { String h = "Hyper"; } class Super extends Hyper { char h = 'h'; } class Test extends Super { public static void main(String[] args) { String s = new Test().h; System.out.println(s); } }
would result in a compile-time error, because
the h
in the source code
for main
would now be construed as referring to the
char
field declared in Super
, and a
char
value
can't be assigned to a String
.
Deleting a field from a
class will break compatibility with any pre-existing binaries that
reference this field, and a NoSuchFieldError
will be thrown when such a
reference from a pre-existing binary is linked. Only private
fields
may be safely deleted from a widely distributed class.
For purposes of binary
compatibility, adding or removing a field f
whose type involves type
variables (§4.4) or
parameterized types
(§4.5) is
equivalent to the addition
(respectively, removal) of a field of the same name whose type is the
erasure (§4.6) of the
type of f
.
If a field that was not
declared final
is changed to be declared final
,
then it can break
compatibility with pre-existing binaries that attempt to assign new
values to the field.
Example 13.4.9-1. Changing A Variable To Be final
class Super { char s; } class Test extends Super { public static void main(String[] args) { Super x = new Super(); x.s = 'a'; System.out.println(x.s); } }
This program produces the output:
a
Suppose that a new version of
class Super
is produced:
class Super { final char s = 'b'; }
If Super
is recompiled but
not Test
, then running the new binary with the
existing binary of Test
results in
a IllegalAccessError
.
Deleting the keyword final
or changing the value to which a field is initialized does not break
compatibility with existing binaries.
If a field is a constant variable
(§4.12.4),
and moreover is
static
, then deleting the keyword final
or
changing its
value will not break compatibility with pre-existing binaries by
causing them not to run, but they will not see any new value for a
usage of the field unless they are recompiled. This result is a
side-effect of the decision to support conditional compilation
(§14.21).
(One might suppose that the new value
is not seen if the usage occurs in a constant expression
(§15.28)
but is seen otherwise. This is not so;
pre-existing binaries do not see the new value at all.)
Another reason for requiring inlining of values of
static
constant variables is because of switch
statements. They
are the only kind of statement that relies on constant expressions,
namely that each case
label of a switch
statement must be a
constant expression whose value is different than every other case
label. case
labels are often references to static
constant
variables so it may not be immediately obvious that all the labels
have different values. If it is proven that there are no duplicate
labels at compile time, then inlining the values into the class
file
ensures there are no duplicate labels at run time either - a very
desirable property.
Example 13.4.9-2. Conditional Compilation
If the example:
class Flags { static final boolean debug = true; } class Test { public static void main(String[] args) { if (Flags.debug) System.out.println("debug is true"); } }
is compiled and executed, it produces the output:
debug is true
Suppose that a new version of
class Flags
is produced:
class Flags { static final boolean debug = false; }
If Flags
is recompiled but
not Test
, then running the new binary with the
existing binary of Test
produces the output:
debug is true
because the value of debug
was a
constant expression, and could have been used in
compiling Test
without making a reference to the
class Flags
.
This behavior would not change
if Flags
were changed to be an interface, as in the
modified example:
interface Flags { boolean debug = true; } class Test { public static void main(String[] args) { if (Flags.debug) System.out.println("debug is true"); } }
Conditional compilation is discussed further at the end of §14.21.
The best way to avoid
problems with "inconstant constants" in widely-distributed code is to
use static
constant
variables only for values which truly are unlikely ever to
change. Other than for true mathematical constants, we recommend that
source code make very sparing use of static
constant variables.
If the read-only nature of final
is required, a
better choice is to declare a private
static
variable and a
suitable accessor method to get its value. Thus we recommend:
private static int N; public static int getN() { return N; }
rather than:
public static final int N = ...;
There is no problem with:
public static int N = ...;
if N
need not be read-only.
We recommend, as a general rule, that only constant expressions be assigned to fields of interfaces.
We note, but do not recommend, that if a field of primitive type of an interface may change, its value may be expressed idiomatically as in:
interface Flags { boolean debug = new Boolean(true).booleanValue(); }
ensuring that this value is not a constant. Similar idioms exist for the other primitive types.
One other thing to note is
that static
constant variables must never appear to
have the default initial value for their type
(§4.12.5).
This means that all such fields appear
to be initialized first during class initialization
(§8.3.2,
§9.3.1,
§12.4.2).
If a field that is not
declared private
was not declared static
and
is changed to be
declared static
, or vice versa, then a linkage error, specifically
an IncompatibleClassChangeError
, will result if the field is used by a
pre-existing binary
which expected a field of the other kind. Such changes are not
recommended in code that has been widely distributed.
Adding or deleting a
transient
modifier of a field does not break compatibility with
pre-existing binaries.
Adding a method or constructor declaration to a class will not break compatibility with any pre-existing binaries, even in the case where a type could no longer be recompiled because an invocation previously referenced a method or constructor of a superclass with an incompatible type. The previously compiled class with such a reference will continue to reference the method or constructor declared in a superclass.
Assume a reference to a
method m
with qualifying type T. Assume further
that m
is in
fact an instance (respectively static
) method declared in a
superclass of T, S.
If a new method of type X
with the same signature and return type as m
is added to a subclass
of S that is a superclass of T or T itself, then a linkage error
may occur. Such a linkage error will occur only if, in addition to the
above, either one of the following is true:
Deleting a method or
constructor from a class may break compatibility with any pre-existing
binary that referenced this method or constructor; a NoSuchMethodError
may
be
thrown when such a reference from a pre-existing binary is
linked. Such an error will occur only if no method with a matching
signature and return type is declared in a superclass.
If the source code for a non-inner class contains no declared constructors, then a default constructor with no parameters is implicitly declared (§8.8.9). Adding one or more constructor declarations to the source code of such a class will prevent this default constructor from being implicitly declared, effectively deleting a constructor, unless one of the new constructors also has no parameters, thus replacing the default constructor. The default constructor with no parameters is given the same access modifier as the class of its declaration, so any replacement should have as much or more access if compatibility with pre-existing binaries is to be preserved.
Adding or removing a type parameter of a method or constructor does not, in itself, have any implications for binary compatibility.
If such a type parameter is used in the type of the method or constructor, that may have the normal implications of changing the aforementioned type.
Renaming a type parameter of a method or constructor has no effect with respect to pre-existing binaries.
Changing the first bound of a type parameter of a method or constructor may change the erasure (§4.6) of any member that uses that type parameter in its own type, and this may affect binary compatibility. Specifically:
If the type parameter is used as the type of a field, the effect is as if the field was removed and a field with the same name, whose type is the new erasure of the type variable, was added.
If the type parameter is used as the type of any formal parameter of a method, but not as the return type, the effect is as if that method were removed, and replaced with a new method that is identical except for the types of the aforementioned formal parameters, which now have the new erasure of the type parameter as their type.
If the type parameter is used as a return type of a method, but not as the type of any formal parameter of the method, the effect is as if that method were removed, and replaced with a new method that is identical except for the return type, which is now the new erasure of the type parameter.
If the type parameter is used as a return type of a method and as the type of one or more formal parameters of the method, the effect is as if that method were removed, and replaced with a new method that is identical except for the return type, which is now the new erasure of the type parameter, and except for the types of the aforementioned formal parameters, which now have the new erasure of the type parameter as their types.
Changing any other bound has no effect on binary compatibility.
Changing the name of a formal parameter of a method or constructor does not impact pre-existing binaries.
Changing the name of a method, or the type of a formal parameter to a method or constructor, or adding a parameter to or deleting a parameter from a method or constructor declaration creates a method or constructor with a new signature, and has the combined effect of deleting the method or constructor with the old signature and adding a method or constructor with the new signature (§13.4.12).
Changing the type of the last formal parameter of a
method from T[]
to a variable arity parameter
(§8.4.1) of
type T (i.e. to T...
),
and
vice versa, does not impact pre-existing binaries.
For purposes of binary
compatibility, adding or removing a method or constructor m
whose
signature involves type variables (§4.4) or
parameterized types (§4.5)
is equivalent to the
addition (respectively, removal) of an otherwise equivalent method
whose signature is the erasure (§4.6) of the
signature of m
.
Changing the result type of
a method, or replacing a result type with void
, or replacing void
with a result type, has the combined effect of deleting the old method
and adding a new method with the new result type or newly void
result (see §13.4.12).
For purposes of binary
compatibility, adding or removing a method or constructor m
whose
return type involves type variables (§4.4) or
parameterized types (§4.5)
is equivalent to the
addition (respectively, removal) of the an otherwise equivalent method
whose return type is the erasure (§4.6) of the
return type of m
.
Changing a method that is
declared abstract
to no longer be declared abstract
does not break
compatibility with pre-existing binaries.
Changing a method that is
not declared abstract
to be declared abstract
will break
compatibility with pre-existing binaries that previously invoked the
method, causing an AbstractMethodError
.
Example 13.4.16-1. Changing A Method To Be abstract
class Super { void out() { System.out.println("Out"); } } class Test extends Super { public static void main(String[] args) { Test t = new Test(); System.out.println("Way "); t.out(); } }
This program produces the output:
Way Out
Suppose that a new version of
class Super
is produced:
abstract class Super { abstract void out(); }
If Super
is recompiled but
not Test
, then running the new binary with the
existing binary of Test
results in an AbstractMethodError
,
because class Test
has no implementation of the
method out
, and is therefore is (or should be)
abstract
.
Changing a method that is
declared final
to no longer be declared final
does not break
compatibility with pre-existing binaries.
Changing an instance method
that is not declared final
to be declared final
may break
compatibility with existing binaries that depend on the ability to
override the method.
Example 13.4.17-1. Changing A Method To Be final
class Super { void out() { System.out.println("out"); } } class Test extends Super { public static void main(String[] args) { Test t = new Test(); t.out(); } void out() { super.out(); } }
This program produces the output:
out
Suppose that a new version of
class Super
is produced:
class Super { final void out() { System.out.println("!"); } }
If Super
is recompiled but
not Test
, then running the new binary with the
existing binary of Test
results in a VerifyError
because
the class Test
improperly tries to override the
instance method out
.
Changing a class (static
)
method that is not declared final
to be declared final
does not
break compatibility with existing binaries, because the method could
not have been overridden.
Adding or deleting a
native
modifier of a method does not break compatibility with
pre-existing binaries.
The impact of changes to
types on pre-existing native
methods that are not recompiled is
beyond the scope of this specification and should be provided with the
description of an implementation. Implementations are encouraged, but
not required, to implement native
methods in a way that limits such
impact.
If a method that is not
declared private
is also declared static
(that
is, a class method)
and is changed to not be declared static
(that is, to an instance
method), or vice versa, then compatibility with pre-existing binaries
may be broken, resulting in a linkage time error, namely an IncompatibleClassChangeError
,
if
these methods are used by the pre-existing binaries. Such changes are
not recommended in code that has been widely distributed.
Adding or deleting a
synchronized
modifier of a method does not break compatibility with
pre-existing binaries.
Changes to the throws
clause of methods or constructors do not break compatibility with
pre-existing binaries; these clauses are checked only at compile
time.
Changes to the body of a method or constructor do not break compatibility with pre-existing binaries.
The keyword final
on a
method does not mean that the method can be safely inlined; it means
only that the method cannot be overridden. It is still possible that a
new version of that method will be provided at link-time. Furthermore,
the structure of the original program must be preserved for purposes
of reflection.
Therefore, we note that a Java compiler cannot expand a method inline at compile time. In general we suggest that implementations use late-bound (run-time) code generation and optimization.
Adding new methods or constructors that overload existing methods or constructors does not break compatibility with pre-existing binaries. The signature to be used for each invocation was determined when these existing binaries were compiled; therefore newly added methods or constructors will not be used, even if their signatures are both applicable and more specific than the signature originally chosen.
While adding a new overloaded method or constructor may cause a compile-time error the next time a class or interface is compiled because there is no method or constructor that is most specific (§<IP_ADDRESS>), no such error occurs when a program is executed, because no overload resolution is done at execution time.
Example 13.4.23-1. Adding An Overloaded Method
class Super { static void out(float f) { System.out.println("float"); } } class Test { public static void main(String[] args) { Super.out(2); } }
This program produces the output:
float
Suppose that a new version of
class Super
is produced:
class Super { static void out(float f) { System.out.println("float"); } static void out(int i) { System.out.println("int"); } }
If Super
is recompiled but
not Test
, then running the new binary with the
existing binary of Test still produces the output:
float
However, if Test
is then
recompiled, using this new Super
, the output is
then:
int
as might have been naively expected in the previous case.
If an instance method is added to a subclass and it overrides a method in a superclass, then the subclass method will be found by method invocations in pre-existing binaries, and these binaries are not impacted.
If a class method is added to a class, then this method will not be found unless the qualifying type of the reference is the subclass type.
Adding, deleting, or changing a static initializer (§8.7) of a class does not impact pre-existing binaries.
Adding or reordering constants in an enum will not break compatibility with pre-existing binaries.
If a pre-existing binary
attempts to access an enum constant that no longer exists, the client
will fail at run time with a NoSuchFieldError
. Therefore such a change is
not
recommended for widely distributed enums.
In all other respects, the binary compatibility rules for enums are identical to those for classes.
This section describes the impact of changes to the declaration of an interface and its members on pre-existing binaries.
Changing an interface that
is not declared public
to be declared public
does not break
compatibility with pre-existing binaries.
If an interface that is
declared public
is changed to not be declared public
, then an
IllegalAccessError
is thrown if a pre-existing binary is linked that needs
but no
longer has access to the interface type, so such a change is not
recommended for widely distributed interfaces.
Changes to the interface
hierarchy cause errors in the same way that changes to the class
hierarchy do, as described in §13.4.4.
In
particular, changes that result in any previous superinterface of a
class no longer being a superinterface can break compatibility with
pre-existing binaries, resulting in a VerifyError
.
Adding an abstract
method to an
interface does not break compatibility with pre-existing
binaries.
A field added to a
superinterface of C may hide a field inherited from a superclass of
C. If the original reference was to an instance field, an IncompatibleClassChangeError
will result. If the original reference was an assignment, an IllegalAccessError
will result.
Deleting a member from an interface may cause linkage errors in pre-existing binaries.
Example 13.5.3-1. Deleting An Interface Member
interface I { void hello(); } class Test implements I { public static void main(String[] args) { I anI = new Test(); anI.hello(); } public void hello() { System.out.println("hello"); } }
This program produces the output:
hello
Suppose that a new version of
interface I
is compiled:
interface I {}
If I
is recompiled but
not Test
, then running the new binary with the
existing binary for Test
will result in a
NoSuchMethodError
.
The effects of changes to the type parameters of an interface are the same as those of analogous changes to the type parameters of a class.
The considerations for
changing field declarations in interfaces are the same as those
for static final
fields in classes, as described in
§13.4.8
and §13.4.9.
The considerations for
changing abstract
method declarations in interfaces include those
for abstract
methods in classes, as described in
§13.4.14, §13.4.15,
§13.4.19,
§13.4.21,
and
§13.4.23.
Adding a default
method,
or changing a method from abstract
to default
,
does not break
compatibility with pre-existing binaries, but may cause an IncompatibleClassChangeError
if a
pre-existing binary attempts to invoke the method. This error occurs
if the qualifying type, T, is a subtype of two interfaces, I
and
J, where both I and J
declare a default
method with the same
signature and result, and neither I nor J is a
subinterface of the
other.
In other words, adding a
default method is a binary-compatible change because it does not
introduce errors at link time, even if it introduces errors at compile
time or invocation time. In practice, the risk of accidental clashes
occurring by introducing a default method are similar to those
associated with adding a new method to a non-final
class. In the
event of a clash, adding a method to a class is unlikely to trigger a
LinkageError
, but an accidental override of the method in a child can lead
to
unpredictable method behavior. Both changes can cause errors at
compile time.
Example 13.5.6-1. Adding A Default Method
interface Painter { default void draw() { System.out.println("Here's a picture..."); } } interface Cowboy {} public class CowboyArtist implements Cowboy, Painter { public static void main(String... args) { new CowboyArtist().draw(); } }
This program produces the output:
Here's a picture...
Suppose that a default method is added
to Cowboy
:
interface Cowboy { default void draw() { System.out.println("Bang!"); } }
If Cowboy
is recompiled but
not CowboyArtist
, then running the new binary with
the existing binary for CowboyArtist
will link
without error but cause an IncompatibleClassChangeError
when main
attempts to invoke draw()
.
Annotation types behave exactly like any other interface. Adding or removing an element from an annotation type is analogous to adding or removing a method. There are important considerations governing other changes to annotation types, such as making an annotation type repeatable (§9.6.3), but these have no effect on the linkage of binaries by the Java Virtual Machine. Rather, such changes affect the behavior of reflective APIs that manipulate annotations. The documentation of these APIs specifies their behavior when various changes are made to the underlying annotation types.
Adding or removing annotations has no effect on the correct linkage of the binary representations of programs in the Java programming language.


coffee
Italian Coffee (Iced/Hot)
- Espresso/Hot Americano 20
- Iced Americano with Orange Peel 40
- Cappuccino 45
- Latte/Mocha 70
- Affogato 55
- Classic Macchiatto 45
- Extra shot 10
Vietnamese Coffee
- Hot/Iced Black Coffee 25
- Hot/Iced Coffee with Condensed Milk 28
- Ha Noi Egg Coffee 70
- Ice Blended Coconut Coffee 70


This potsage [sic] has been removed because:
(a) The Dobson estate slapped me with a cease-and-desist order
(b) There are far too many out of print Dobson pamphlets to choose from
(c) I haven't got the faintest idea how to replicate the format of those immensely irritating quizzes that have spread across Het Internet like an ague
(d) All of the above
(e) None of the above


modbus: Modbus Support - Release Notes
Release 3-0
August 9, 2019
- This is a major rewrite of the driver, changing from C to C++ inheriting from
asynPortDriver. The major reason for the change is to be able to invoke the class
methods such as drvModbusAsyn::doModbusIO() from other drivers. This allows drivers
that communicate with Modbus devices to call methods to perform the Modbus I/O directly,
which was not possible with the previous C API.
- The new driver should be completely backwards compatible with previous versions,
so no changes to IOC startup scripts or OPI displays are required.
- A new example program, testClient.cpp, has been added. This program demonstrates
how to instantiate a drvModbusAsyn object and use it to perform Modbus I/O to an
external device. This example is a pure C++ application running without an IOC.
The same code could be used in a driver in an IOC.
- Added OPI autoconvert files for edm, CSS BOY, and caQtDM.
Release 2-11
June 28, 2018
- Fixed problem with asynPrintIO. It was printing too few bytes under some conditions.
- Added a counter of the number of I/O errors since the last successful I/O to the
device. This counter is printed when the I/O returns to normal, and the counter
is reset. Thanks to Scott Stubbs and Bruce Hill for this.
Release 2-10-1
September 15, 2017
- Previously the Modbus/TCP transaction ID was set to 1 on writes and ignored on
reads. Now the Modbus transaction ID is incremented by 1 on each message sent by
the driver. On the reply the driver waits until a message with the correct transaction
ID is received, ignoring messages with any other transaction ID. This is more robust,
especially for Modbus/UDP links. Thanks to Eric Norum for this.
Release 2-10
September 15, 2017
- Added an epicsAtExit handler which stops the poller task. This eliminates error
messages from the poller task when the IOC exits.
- Greatly reduced the number of error messages printed when there are communication
errors or timeouts with the Modbus device. Previously error messages were printed
for each asyn port on each poller loop. Now, there is a single message per port
when an error condition is detected, and a single message when the error condition
ends. For example this is the output from a Koyo PLC with 3 drvModbusAsyn input
ports that are polling at 10 Hz. The drvAsynIPPort has the asynOption "disconnectOnReadTimeout"
set to "Y" set so that it reconnects quickly when the network is reconnected. The
Ethernet cable was unplugged for about 8 seconds and then plugged back in.
2017/09/12 13:14:25.764 drvModbusAsyn::doModbusIO port K1_Xn_Bit error calling writeRead, error=<IP_ADDRESS>:502 read error: S_errno_EWOULDBLOCK, nwrite=6/6, nread=0
2017/09/12 13:14:25.781 drvModbusAsyn::doModbusIO port K1_Yn_In_Bit error calling writeRead, error=asynManager::queueLockPort queueRequest timed out, nwrite=0/6, nread=0
2017/09/12 13:14:25.814 drvModbusAsyn::doModbusIO port K1_Cn_In_Bit error calling writeRead, error=asynManager::queueLockPort queueRequest timed out, nwrite=0/6, nread=0
2017/09/12 13:14:33.564 drvModbusAsyn::doModbusIO port K1_Xn_Bit writeRead status back to normal nwrite=6/6, nread=6
2017/09/12 13:14:33.581 drvModbusAsyn::doModbusIO port K1_Yn_In_Bit writeRead status back to normal nwrite=6/6, nread=6
2017/09/12 13:14:33.614 drvModbusAsyn::doModbusIO port K1_Cn_In_Bit writeRead status back to normal nwrite=6/6, nread=34
The first error message indicates that the K1_Xn_Bit driver got a timeout when reading
the device. That timeout causes drvAsynIPPort driver to disconnect the port because
of the asynOption described above. The next 2 errors are from the other two ports
which get queueRequest timeouts because the port is now disconnected. When the network
cable is reconnected each of the ports prints a "status back to normal" message.
- The example IOC startup scripts now call asynSetOption("disconnectOnReadTimeout",
"Y"). for TCP ports. This ensures much faster reconnection when the Modbus device
comes back online if it disconnects. Note that for this to work asyn R4-32 or later
is needed, because of a needed fix to asynManager::queueLockPort(), which is used
by the asynXXXSyncIO functions.
Release 2-9
August 23, 2016
- Added support for specifying absolute Modbus addresses in the asyn "addr" field.
Previously each driver was limited to addressing at most 125 registers (read operations)
or 123 registers (write operations), and the asyn "addr" field specified an offset
relative to the modbusStartAddress passed as the fifth argument to drvAsynModbusConfigure().
Now if the modbusStartAddress=-1 then the driver will use absolute addressing, and
the asyn "addr" specifies the absolute Modbus address in the range 0 to 65535. In
this case the modbusLength argument to drvAsynModbusConfigure() is the maximum length
required for any single Modbus transaction by that driver. This would be 1 if all
Modbus reads and writes are for 16-bit registers, but it would be 4 if 64-bit floats
(4 16-bit registers) are being used, and 100 (for example) if an Int32 waveform
record with NELM=100 and a 16-bit Modbus data type is being read or written.
- Added support for strings using the asynOctet interface. Some vendors use Modbus
for string data. The Modbus standard does not specify how strings should be stored,
and different vendors do it differently. The driver supports 4 new Modbus data types,
using the following drvInfo strings:
- STRING_HIGH One character is stored in the high byte of each register.
- STRING_LOW One character is stored in the low byte of each register.
- STRING_HIGH_LOW Two characters are stored in each register, the first in the high
byte and the second in the low byte.
- STRING_LOW_HIGH Two characters are stored in each register, the first in the low
byte and the second in the high byte.
The standard asynOctet device support is used. This supports stringout, stringin,
and waveform records. Waveform records can be used for either input or output. The
stringin and stringout records are limited to 40 characters strings, while waveform
records are limited only by the 125/123 register limit of Modbus for read/write
operatations i.e. up to 250 characters.
- Added error checking to make sure the length of the reply from the Modbus server
is the expected value. Previously garbled communications could cause a crash because
of array bounds violation.
Release 2-8
February 11, 2016
- Improved the logic in the poller thread.
- Previously there was a call to epicsThreadSleep at the end of the poller loop.
This meant that the poller always ran at least once, which might not be desirable.
It also meant that one could only temporarily disable the poller by calling pasynManager->lockPort(),
or to make it run infrequently by setting POLL_DELAY to a long sleep time. In R2-5
the MODBUS_READ command was added. However, this just did a read operation without
triggering the poller thread, so records with I/O Intr scan would not process. It
also required disabling the poller thread with pasynManager->LockPort.
- The poller thread has been changed so that it calls epicsEventWait or epicsEventWaitWithTimeout
at the beginning of the poller loop. If the POLL_DELAY is >0 then it calls epicsEventWaitWithTimeout,
using the POLL_DELAY as the timeout. If POLL_DELAY is ≤0 then it calls epicsEventWait,
with no timeout. This allows the poller to be suspended indefinitely and only run
when the epicsEvent is signaled. It also means if POLL_DELAY ≤0 then the poller
does not run at all until the epicsEvent is signaled. The MODBUS_READ command now
signals the epicsEvent to trigger the poller, rather than directly doing a read
operation.
- Added a new template file, poll_trigger.template which loads a record with the
MODBUS_READ drvInfo string to trigger the poller.
- Fixed problems when using waveform records with the asynInt32Array interface.
- If the Modbus data type was not UINT16 or INT16 then the data were not read into
the waveform record correctly. This bug has been fixed, and new files iocBoot/iocTest/array_test.cmd
and array_test.substitions were added to test it using the Modbus Slave Simulator.
New adl files, modbusTestArray.adl and array_test.adl were added for these tests.
modbusTestArray.adl also contains controls for POLL_DELAY and POLL_TRIGGER to verify
the improvements to the poller thread above.
- Previously the first element in the waveform record was always the first register
in that modbus driver, i.e. it did not allow specifying a register offset using
the asyn ADDR address. This was improved so now one can specify an offset using
the OFFSET macro parameter, as with the non-array records in modbus.
Release 2-7-1
May 6, 2015
- Minor changes to allow building dynamically on Cygwin.
Release 2-7
August 19, 2014
- Added support for function code 23, Read/Write Multiple Registers. Because of
the architecture of the EPICS Modbus support, a driver that uses function 23 is
restricted to being either a read-only driver where no data is written, or a write-only
driver where no data is read. It is preferable to use functions codes 3, 4, 6, or
16 if they are supported. However, some older devices only support function code
23.
Release 2-6
April 10, 2014
- Fixed a problem with function code 6 (write single register) on Wago devices when
the MASK was not 0x0 or 0xFFFF. In this case a read/modify/write operation is required,
and the driver was reading from the wrong address. On Wago devices the readback
address for a register is offset by 0x200 from the write address. This offset was
not being applied.
- Added 2 new records to statistics.template. $(P)$(R)HistTimeAxis is a waveform
record that contains the X time axis for histogram plots, and MsPerBin is the time
per histogram bin in msec.
Release 2-5
October 2, 2013
- Fixed a mutex issue with the input poller. Previous versions had a potential problem
with input records which did not use Scan=I/O Intr. In this case there was no mutex
protecting access to the read buffer between the device support thread and the poller
thread that was reading the Modbus data. It is likely that this problem was rarely
if ever encountered because input records were almost always I/O Intr scanned. The
fix was to remove the existing mutex in the driver and to simply call pasynManager->lockPort()
for the entire duration of the polling cycle. This fix has a positive side effect:
it is now possible for external clients, for example other drivers that call the
Modbus driver, to temporarily disable the poller by calling pasynManager->lockPort().
This can be used to allow atomic sequences of Modbus read/write operations with
no possibility of interference from the poller.
- The above fix means that Modbus input drivers can now block on read operations,
because they may need to wait for the poller to complete. These drivers are thus
now created with the ASYN_CANBLOCK flag, and the documentation has been changed
to state that input drivers are now asynchronous, not synchronous.
- Changed the wait for interruptAccept in the poller thread. It now works to have
a very long polling time, but still having one initial read cycle and callback on
I/O Intr scanned records.
- Added a new command to the Modbus driver, which has the drvUser string "MODBUS_READ".
This command is implemented on the asynInt32 interface. Calling asynInt32->write()
with this command causes a Modbus I/O cycle for this driver. It will typically be
used to force a Modbus input driver to do a read operation independent of the poller
thread.
- Added a new test driver called testModbusSyncIO.cpp. This driver is derived from
asynPortDriver. It is designed to demonstrate using another asyn driver to communicate
through the Modbus driver. This concept is currently being used to have a motor
driver that communicates through the Modbus driver. It demonstrates two types of
communication:
- Using pasynInt32SyncIO calls to make "synchronous" calls to the Modbus driver.
These calls are blocking and are queued.
- Doing an atomic "read/modify/write" cycle using the following sequence of operations.
This uses pasynManager->lockPort to disable the poller and allow this thread to
directly call the asynInt32->write() and asynInt32->read() functions. It uses the
following logic:
- Lock the output port. Needed because we are directly calling the write() function.
- Lock the input port, which disables the poller and because we are directly calling
read().
- Sleep for 1 second so we can prove the poller was idle.
- Force a read of the Modbus input by writing on asynInt32 interface with the MODBUS_READ
drvInfo.
- Read the input value on the asynInt32 interface.
- Add the value passed to this function to the current value just read.
- Sleep for 2 seconds so we can prove the poller was idle.
- Write the new value to the output driver.
- Unlock the input port.
- Unlock the output port.
This new example driver can be tested with the new files iocBoot/iocTest/sim3.cmd
and sim3.substitutions.
- Minor change to structure packing declarations to allow building on ARM architecture
with GCC 4.2.1
Release 2-4
May 22, 2012
- Improved support for different Modbus data types.
- Added support for 7 new Modbus data types:
- 16-bit signed integer
- 32-bit integer, little-endian
- 32-bit integer, big-endian
- 32-bit float, little-endian
- 32-bit float, big-endian
- 64-bit float, little-endian
- 64-bit float, big-endian
- Added support to allow the Modbus data type to be specified on a per-record bas
using the asyn drvUser field. Previously all records connected to a Modbus asyn
port driver had the same Modbus data type, because each Modbus port driver was limited
to a single data type.
- If the drvUser string in the link specification is omitted, or if it is the default
of MODBUS_DATA then the record will use the modbusDataType defined in drvModbusAsynConfigure.
This is backwards compatible, and existing IOCs will continue to work with no changes
to databases or startup scripts.
- Added new example IOC script, substitutions file, and medm screens to test all
data types. The testing was done with a Modbus slave simulator.
- Added special treatment for Wago devices. These device are different from other
Modbus devices because the address to read back a register is not the same as the
address to write the register. The readback address is the write address plus 0x200.
This means that in previous versions of this driver the initial readback value for
Modbus write operations to Wago devices was incorrect. This was fixed by adding
the 0x200 offset to the readback address if the plcType argument to drvModbusAsynConfigure
contains the substring "Wago" (case sensitive).
- Added support for passing status information back to device support in callbacks
for I/O Intr scanned records. Support for this was added in asyn R4-19. This means
that if the Modbus device communications returns errors that I/O Intr scanned records
will now have their alarm status set correctly.
Release 2-3
Sept 9, 2011
- build changes
- added .opi files for CSS-BOY
Release 2-2
March 13, 2011
- R2-0 introduced a 20 ms delay before each Modbus write. This was not correct,
delays should only be needed for serial RTU. The Modicon Modbus Protocol Reference
Guide says this must be at least 3.5 character times, e.g. about 3.5ms at 9600 baud,
for Serial RTU. An additional writeDelayMsec parameter was added to modbusInterposeConfig.
It is the final parameter, so if it is not specified the default value is zero,
which is appropriate when not using Serial RTU. In the startup script lines like
the following:
modbusInterposeConfig("Koyo1",0,5000)
should be changed to:
modbusInterposeConfig("Koyo1",0,5000,0)
for no delay, or to:
modbusInterposeConfig("Koyo1",0,5000,20)
for a 20 ms delay.
Release 2-1
November 7, 2010
- Bug fix. Non-automatic connection to the Modbus server uses pasynCommonSyncIO->connectDevice().
The pasynUser being used for that operation was being created with pasynOctetSyncIO->connect().
That was always an error, it must be created with pasynCommonSyncIO->connect().
This error became serious with asyn R4-14, and non-automatic connection no longer
worked.
- Previous releases of modbus recommended setting NoAutoConnect=1 when configuring
the TCP or serial port. That was probably because of problems in connection management
in earlier versions of asyn. With asyn R4-14 this is no longer necessary, and NoAutoConnect=0,
the normal default, can be used with no problems. The example scripts Koyo1.cmd
and Koyo2.cmd in the iocBoot directory have been changed to enable automatic connection
to the IP or serial driver.
Release 2-0
November 26, 2009
- Moved the slave address handling from the asynInterpose layer to the Modbus driver
layer. This was done because handing it in the the interpose layer only allowed
1 slave address per asyn serial port or IP port. This did not allow a single serial
port to be used with multiple Modbus devices on an RS-485 bus, for example. NOTE:
This requires all startup command scripts to be changed, because the syntax of the
modbusInterposeConfig and drvModbusAsynConfigure commands has changed. Thanks
to Yves Lussignol from CEA in France for making these changes.
Release 1-3
September 19, 2008
- Changed modbusInterpose.c to replace pasynOctet->writeRaw() and pasynOctet->readRaw()
with pasynOctet->write() and pasynOctet->read(), because the raw routines have been
removed in asyn R4-10.
- Changed the driver to use the asynStandardInterfaces interfaces added to asyn
in R4-10.
Release 1-2
September 6, 2007
- Fixed bug in computing byteCount in WRITE_MULTIPLE_COILS function code.
There is a known limitation with using serial interfaces. It is not currently possible
to have multiple Modbus servers connected to a single serial port. This is a limitation
of the asynInterposeInterface architecture used. It is fixed in release 2-0.
Release 1-1
April 30, 2007
Initial release of modbus module.


Privacy Policy
Information Collection
Information Usage
Information Protection
Cookie Usage
3rd Party Disclosure
3rd Party Links
Google AdSense
Fair Information Practices
Fair
information
Practices
Practices
COPPA
CalOPPA
Our Contact Information
This privacy policy has been compiled to better serve those who
are concerned with how
their 'Personally identifiable information' (PII) is being used online. PII, as used
in US privacy law and
information security, is information that can be used on its own or with other
information to identify,
contact, or locate a single person, or to identify an individual in context. Please
read our privacy policy
carefully to get a clear understanding of how we collect, use, protect or otherwise
handle your Personally
Identifiable Information in accordance with our website.
What personal information do we collect from the people
that visit our blog,
website or app?
When ordering or registering on our site, as appropriate, you may
be asked to enter your
name, email address, Date of Birth, Gender, Country or other details to help you
with your experience.
When do we collect information?
We collect information from you when you register on our site or
enter information on our
site.
How do we use your information?
We may use the information we collect from you when you
register, make a purchase, sign
up for our newsletter, respond to a survey or marketing communication, surf the
website, or use certain
other site features in the following ways:
• To
personalize user's experience
and to allow us to deliver the type of content and product offerings in which you
are most interested.
• To improve
our website in order to
better serve you.
• To allow
us to better service you
in responding to your customer service requests.
• To
administer a contest,
promotion, survey or other site feature.
How do we protect visitor information?
We do not use vulnerability scanning and/or scanning to PCI
standards.
We use regular Malware Scanning.
Your personal information is contained behind secured networks
and is only accessible by
a limited number of persons who have special access rights to such systems, and are
required to keep the
information confidential. In addition, all sensitive/credit information you supply
is encrypted via Secure
Socket Layer (SSL) technology.
We implement a variety of security measures when a user enters,
submits, or accesses
their information to maintain the safety of your personal information.
All transactions are processed through a gateway provider and are
not stored or processed
on our servers.
Do we use 'cookies'?
Yes. Cookies are small files that a site or its service provider
transfers to your
computer's hard drive through your Web browser (if you allow) that enables the
site's or service provider's
systems to recognize your browser and capture and remember certain information. For
instance, we use cookies
to help us remember and process the items in your shopping cart. They are also used
to help us understand
your preferences based on previous or current site activity, which enables us to
provide you with improved
services. We also use cookies to help us compile aggregate data about site traffic
and site interaction so
that we can offer better site experiences and tools in the future.
We use cookies to:
• Understand
and save user's
preferences for future visits.
• Keep track
of advertisements.
• Compile
aggregate data about site
traffic and site interactions in order to offer better site experiences and tools in
the future. We may also
use trusted third party services that track this information on our behalf.
You can choose to have your computer warn you each time a cookie is being sent, or you can choose to turn off all cookies. You do this through your browser (like Internet Explorer) settings. Each browser is a little different, so look at your browser's Help menu to learn the correct way to modify your cookies.
If you disable cookies off, some features will be disabled It
won't affect the users
experience that make your site experience more efficient and some of our services
will not function
properly.
However, you can still place orders .
Third Party Disclosure
We do not sell, trade, or otherwise transfer to outside parties
your personally
identifiable information unless we provide you with advance notice. This does not
include website hosting
partners and other parties who assist us in operating our website, conducting our
business, or servicing
you, so long as those parties agree to keep this information confidential. We may
also release your
information when we believe release is appropriate to comply with the law, enforce
our site policies, or
protect ours or others' rights, property, or safety.
However, non-personally identifiable visitor information may be provided to other parties for marketing, advertising, or other uses.
However, non-personally identifiable visitor information may be provided to other parties for marketing, advertising, or other uses.
Third party links
Occasionally, at our discretion, we may include or offer third
party products or services
on our website. These third party sites have separate and independent privacy
policies. We therefore have no
responsibility or liability for the content and activities of these linked sites.
Nonetheless, we seek to
protect the integrity of our site and welcome any feedback about these sites.
Google
Google's advertising requirements can be summed up by Google's
Advertising Principles.
They are put in place to provide a positive experience for users.
https://support.google.com/adwordspolicy/answer/1316548?hl=en
We have not enabled Google AdSense on our site but we may do so
in the future.
California Online Privacy Protection Act
CalOPPA is the first state law in the nation to require
commercial websites and online
services to post a privacy policy. The law's reach stretches well beyond California
to require a person or
company in the United States (and conceivably the world) that operates websites
collecting personally
identifiable information from California consumers to post a conspicuous privacy
policy on its website
stating exactly the information being collected and those individuals with whom it
is being shared, and to
comply with this policy. - See more at:
http://consumercal.org/california-online-privacy-protection-act-caloppa/#sthash.0FdRbT51.dpuf
According to CalOPPA we agree to the following:
Users can visit our site anonymously
Once this privacy policy is created, we will add a link to it on
our home page, or as a
minimum on the first significant page after entering our website.
Our Privacy Policy link includes the word 'Privacy', and can be
easily be found on the
page specified above.
Users will be notified of any privacy policy changes:
• On our
Privacy Policy Page
Users are able to change their personal information:
• By
emailing us
• By logging
in to their account
How does our site handle do not track signals?
We honor do not track signals and do not track, plant cookies, or
use advertising when a
Do Not Track (DNT) browser mechanism is in place.
Does our site allow third party behavioral tracking?
It's also important to note that we allow third party behavioral
tracking
COPPA (Children Online Privacy Protection Act)
When it comes to the collection of personal information from
children under 13, the
Children's Online Privacy Protection Act (COPPA) puts parents in control. The
Federal Trade Commission, the
nation's consumer protection agency, enforces the COPPA Rule, which spells out what
operators of websites
and online services must do to protect children's privacy and safety online.
We do not specifically market to children under 13.
Fair Information Practices
The Fair Information Practices Principles form the backbone of
privacy law in the United
States and the concepts they include have played a significant role in the
development of data protection
laws around the globe. Understanding the Fair Information Practice Principles and
how they should be
implemented is critical to comply with the various privacy laws that protect
personal information.
In order to be in line with Fair Information Practices we
will take the following
responsive action, should a data breach occur:
We will notify the users via email
• Other
Within 3 business days
We will notify the users via in site notification
• Other
Within 2 business days
We also agree to the individual redress principle, which requires that individuals have a right to pursue legally enforceable rights against data collectors and processors who fail to adhere to the law. This principle requires not only that individuals have enforceable rights against data users, but also that individuals have recourse to courts or a government agency to investigate and/or prosecute non-compliance by data processors.
CAN SPAM Act
The CAN-SPAM Act is a law that sets the rules for commercial
email, establishes
requirements for commercial messages, gives recipients the right to have emails
stopped from being sent to
them, and spells out tough penalties for violations.
We collect your email address in order to:
• Send
information, respond to
inquiries, and/or other requests or questions.
To be in accordance with CANSPAM we agree to the following:
• NOT use
false, or misleading
subjects or email addresses
• Identify
the message as an
advertisement in some reasonable way
• Include
the physical address of
our business or site headquarters
• Monitor
third party email
marketing services for compliance, if one is used.
• Honor
opt-out/unsubscribe requests
quickly
• Allow
users to unsubscribe by
using the link at the bottom of each email
If at any time you would like to unsubscribe from receiving future emails, you can email us at
• Follow the
instructions at the
bottom of each email.
and we will promptly remove you from ALL correspondence.
PROTECTION OF INTELLECTUAL PROPERTY
The USER/CUSTOMER shall not acquire any intellectual property rights
under this Agreement except
the limited right to use IndieWise. The User/Customer acknowledges that the Owner
reserves the rights to all related
copyrights, trademarks, patents, and intellectual property concerning IndieWise. Such
copyrights, trademarks,
patents, and intellectual property are (and at all times will be) the property of the
Owner, even if suggestions,
comments, and/or ideas made by the User/Customer are incorporated into IndieWise or
related materials during the
period of this Agreement.
INDEMNITY
Each party agrees to defend, indemnify, and hold harmless the other
party and its officers,
directors, agents, affiliates, distributors, representatives, and employees from any and
all third party claims,
demands, liabilities, costs and expenses, including reasonable attorney's fees, costs
and expenses resulting from
the indemnifying party's material breach of any duty, representation, or warranty under
this Agreement. In any legal
action between the parties concerning this Agreement, the prevailing party shall be
entitled to recover reasonable
attorney fees and costs.
Contacting Us
If there are any questions regarding this privacy policy you may
contact us using the information
below.
http://getindiewise.com
705-266 Sherbourne St
Toronto, Ontario M5A2S2
Canada
<EMAIL_ADDRESS>Last Edited on 2016-01-09


The goal of DatawRappr is to provide a wrapper for Datawrapper’s API to connect data from R directly with Datawrapper’s charts capabilities. It uses the new API version 3.0.
datawrapper_auth()
dw_data_to_chart()
All functions (except datawrapper_auth()
) are preceded by dw_
:
dw_test_key()
dw_list_charts()
.dw_list_folders()
.dw_create_chart()
dw_data_to_chart()
dw_retrieve_chart_metadata()
) or edits metadata, description and visualization of an existing chart via dw_edit_chart()
dw_publish_chart()
dw_delete_chart()
Right now this package is experimental and only available on Github:
# install.packages("devtools") # if not already installed on your system
devtools::install_github("munichrocker/DatawRappr")
library(DatawRappr)
To use the API you have to create an API key on Datawrapper.
Click on Dashboard - Settings and move down to the section that says API Access Tokens.
Click on Create new personal access token, enter a name and save the token:
Copy the API key in the clipboard and use
datawrapper_auth(api_key = "12345678")
to save the key to our system. If a key already exists, you may add the argument overwrite = TRUE
to datawrapper_auth()
.
To make sure, your key is working as expected, you can run
dw_test_key()
with no arguments. It will then use the saved key from the environment. If the key is correct, you will receive a response from the API with personal details about your account - a dw_user
-object that has no further use than to check your key.
Note: If you want to see your currently saved API key, you may use the helper function dw_get_api_key()
.
Congratulations, you’re good to go!
To create an empty chart, you can use
dw_create_chart()
which will use the API key stored locally on your system.
By default, Datawrapper will create an empty linechart, without a title. The function returns a dw_chart-object with the metadata-elements from the API. This object can be used to populate the chart_id
-argument in all other functions - which means you don’t have to deal with it. Just store the response from dw_create_chart()
in your R-environment.
To populate that chart with data, you can run
dw_data_to_chart(x = DATAFRAME, chart_id = CHART_ID_OR_dw_chart-object)
which uploads an R data.frame to an Datawrapper chart.
The data.frame should already be in the right format, only including the expected columns for the chart. The API will asume, that the first row contains headers. If that’s not true, you have to edit the metadata afterwards:
dw_edit_chart(chart_id = CHART_ID_OR_dw_chart-object, title = "I'm a title",
data = list(`horizontal-header` = "false"))
Datawrapper offers a lot of variability in editing it’s charts’ metadata. You can find a whole Documentation here.
To speed things up, the dw_edit_chart()
-function has some built-in arguments for common transformations:
title
intro
which is the text below the titleannotate
which is the text below the plotsource_name
which states the sourcesource_url
which links to the source - but only if a source_name
is provided.If you want to edit specific arguments in your plot, you can use the arguments data
, visualize
, describe
and publish
to include lists to the API call which change all possible settings in a chart, as shown in the example above.
When you’re finished editing your chart, you might want to publish it:
dw_publish_chart(chart_id = CHART_ID_OR_dw_chart-object)
This function returns a URL to the chart and the embed code, if you set the argument return_urls to TRUE
.
Or you might want to delete a chart:
dw_delete_chart(chart_id = CHART_ID_OR_dw_chart-object)
This package makes heavy use of the httr
-package, which on itself is a wrapper of the curl-package.
There is a API-documentation and a Getting Started guide with examples from Datawrapper.
A full Package-documentation can be found here.


-- ### Last week: RStudio IDE and GitHub account -- ### This week: R and git installation -- ### Links are on the page hosting this video ### Reference: *Happy Git and GitHub for the useR*, Jenny Bryan et al. ### https://happygitwithr.com/ --- class: inverse # Questions? Problems? -- ## If **you** are a part of Uncoast Unconf 2019: -- ### Check then, create a new GitHub issue: `https://github.com/uncoast-unconf/uu-2019-day-zero/issues` -- ## OR ### Email Ian Lyttle at ian [dot] lyttle [at] se [dot] com --- # Why are we doing this? -- ### We want all of our computers to be ready to collaborate -- ### Package-development tools work best using recent versions of R -- ### To move code to/from GitHub, we need git --- # Goals for this week ### R - what version do you have? In the **R console** pane: ```r R.version.string ``` -- ``` ## [1] "R version 3.5.3 (2019-03-11)" ``` - if you have R 3.5.3, **you're done!** -- - if you have 3.4 <= R <= 3.5.2, up to you... -- - if you have R < 3.4, **please update!** -- - to get the latest version of R: - go to [https://cloud.r-project.org/](https://cloud.r-project.org/) - follow the *Download and Install R* link for Windows - if you update more than a minor version, e.g. 3.4 -> 3.5, you have to reinstall your R packages --- # Goals for this week ### git installation - do you have git installed? In the **Terminal** pane: ``` git --version ``` -- ``` git version 2.21.0.windows.1 ``` - if you get a response like this, **you're done!** -- - if not, we suggest you install Git for Windows: [https://gitforwindows.org/](https://gitforwindows.org/) - When asked about "Adjusting your PATH environment": - select "Git from the command line and also from 3rd-party software". - Otherwise, select the defaults. --- class: inverse # Questions? Problems? -- ## If **you** are a part of Uncoast Unconf 2019: -- ### Check then, create a new GitHub issue: `https://github.com/uncoast-unconf/uu-2019-day-zero/issues` -- ## OR ### Email Ian Lyttle at ian [dot] lyttle [at] se [dot] com --- class: inverse, center, middle # Thanks, see you next week!


Jeremiah 36:17
And they asked Baruch, saying, Tell us now, How didst thou write all these words at his mouth?


---
layout: post
status: publish
published: true
title: How to Construct Clones of Non-Clonable Objects Or Construct Domain Objects
Dynamically from Struts Forms
author:
display_name: Gunith
login: admin
email<EMAIL_ADDRESS>url: ''
author_login: admin
author_email<EMAIL_ADDRESS>wordpress_id: 62
wordpress_url: http://www.gunith.com/?p=62
date: '2010-11-27 09:12:10 +0530'
date_gmt: '2010-11-27 03:42:10 +0530'
categories:
- Java
- Struts
tags:
- programming
- Java
- code
- jee
- coding
- struts
- commons
- beanutils
- beanutilbeans
- propertyutils
- propertyutilbeans
- clonable
- clone
excerpt_separator:
comments:
- id: 9
author: Guus Bloemsma
author_email<EMAIL_ADDRESS>author_url: http://www.bloemsma.net
date: '2010-11-27 17:05:33 +0530'
date_gmt: '2010-11-27 11:35:33 +0530'
content: "These methods rely on reflection to copy all properties. This means they
circumvent all business logic with all the consequences that can have.\r\n\r\nI
understand that sometimes such a hack is necessary, but for maintainability's
sake you are much better off introducing a copy constructor."
- id: 10
author: Gunith
author_email<EMAIL_ADDRESS>author_url: ''
date: '2010-11-27 18:15:19 +0530'
date_gmt: '2010-11-27 12:45:19 +0530'
content: Thanks adding your very valid point of view.. It is very true that we would
have customisations when we copy properties from one object to the other. If a
Bean has 10 attributes, the idea is to do all 10 in one go using this method and
thereafter set the odd 3 attributes using setters and custom validations and type
conversions
- id: 59
author: Martin
author_email<EMAIL_ADDRESS>author_url: ''
date: '2010-12-02 13:53:30 +0530'
date_gmt: '2010-12-02 08:23:30 +0530'
content: "This approach only works well for \"simple\" properties. It becomes questionable
for collection properties, as you have to build a \"shadow\" copy of the business
object in the ActionForm. In any case you to expose detailed knowledge of the
structure of your business objects to the view layer. \r\nAnd you circumvent any
access- and validity control that might need to be applied in your business logic
(as Guus already said). \r\nUnder these circumstances you could fill the business
objects themselves in the ActionForm (which you should really not do), avoiding
the #copyProperties step alltogether."
- id: 81
author: Gunith
author_email<EMAIL_ADDRESS>author_url: ''
date: '2010-12-04 08:30:43 +0530'
date_gmt: '2010-12-04 03:00:43 +0530'
content: "Hi Martin, Thanks to you for adding your valid point of view. \r\n\r\nI
actually tested your theory to test if the code above creates a security problem.
Here are the details. \r\n\r\nI made 2 classes, one the bean class the other the
testing class.\r\nThe Bean Class >>\r\n\r\npackage
gunith.evillab;\r\n\r\npublic class SampleBean {\r\n private boolean noGetter;\r\n
\ private boolean getterSetter;\r\n\r\n /**\r\n * @param noGetter
the noGetter to set\r\n */\r\n public void setNoGetter(boolean noGetter)
{\r\n this.noGetter = noGetter;\r\n }\r\n\r\n /**\r\n * @return
the getterSetter\r\n */\r\n public boolean isGetterSetter() {\r\n return
getterSetter;\r\n }\r\n\r\n /**\r\n * @param getterSetter the getterSetter
to set\r\n */\r\n public void setGetterSetter(boolean getterSetter)
{\r\n this.getterSetter = getterSetter;\r\n }\r\n\r\n /* (non-Javadoc)\r\n
\ * @see java.lang.Object#toString()\r\n */\r\n\r\n @Override\r\n
\ public String toString() {\r\n StringBuilder builder = new StringBuilder();\r\n
\ builder.append(\"SampleBean [noGetter=\");\r\n builder.append(noGetter);\r\n
\ builder.append(\", getterSetter=\");\r\n builder.append(getterSetter);\r\n
\ builder.append(\"]\");\r\n return builder.toString();\r\n }\r\n}
\r\n\r\nThen
the test class >>\r\n\r\npackage gunith.evillab;\r\n\r\nimport
java.lang.reflect.InvocationTargetException;\r\nimport org.apache.commons.beanutils.BeanUtilsBean;\r\n\r\npublic
class SampleBeanCopier {\r\n\r\n public static void main(String[] args) {\r\n
\ SampleBean src = new SampleBean();\r\n SampleBean dest = new SampleBean();\r\n
\ \r\n src.setGetterSetter(true);\r\n src.setNoGetter(true);\r\n\r\n
\ BeanUtilsBean bub = new BeanUtilsBean();\r\n\r\n try {\r\n bub.copyProperties(dest,
src);\r\n }\r\n catch (IllegalAccessException e) {\r\n e.printStackTrace();\r\n
\ }\r\n catch (InvocationTargetException e) {\r\n e.printStackTrace();\r\n
\ }\r\n System.out.println(dest.toString());\r\n }\r\n}
\r\n\r\nWell,
the result is that the boolean without the getter (noGetter) has false while getterSetter
returns true. That shows that class the BeanUtilBeans uses getters and setters
instead of the private properties.\r\n\r\nAs for the one about the 'shadow collection'
theory I can't figure out sure how to test. We can't do a hashCode test, can we?"
---
To Construct Domain Objects Dynamically from Struts Forms was a problem in the project I was working in. This is an extension in making Clones of non-Clonable Objects. So, we did a little bit of research and found a way to do this,
In Apache Commons BeanUtils, there exists the class BeanUtilsBean. Check the following method,
org.apache.commons.beanutils.BeanUtilsBean#copyProperties(destBean, origBean)Copy property values from the origin bean to the destination bean for all cases where the property names are the same. For each property, a conversion is attempted as necessary. All combinations of standard JavaBeans and DynaBeans as origin and destination are supported. Properties that exist in the origin bean, but do not exist in the destination bean (or are read-only in the destination bean) are silently ignored.
The other day, I found the Official Struts option for this while I was browsing through the Struts 1.x documentation for ActionForm. It talks about this method which does pretty much the same,
org.apache.commons.beanutils.PropertyUtils.copyProperties(actionForm, businessObject);