Hi Patrick,

Works for me.

Craig

On Sep 24, 2007, at 3:34 PM, Patrick Linskey wrote:

Seems fair enough to me. We could simplify this by making
J2DoPriv5Helper extend J2DoPrivHelper, in which case developers can
stick with a single class within a given module.

-Patrick

On 9/24/07, Albert Lee <[EMAIL PROTECTED]> wrote:
2) If we put the new 1.5 functions in lib-5, then we need a different
class
e.g. J2DoPriv15Helper.java. This makes an unnatural feel of using the
J2DoPrivHelper.

How is that unnatural?

What I mean is if J2DoPrivHelper is split to 2 classes, developers will need to call J2DoPrivHelper.getXXXAction() or J2DoPriv5Helper.getXXXAction()
depends on if the action is 1.5 specific.

Albert Lee.

On 9/24/07, Patrick Linskey <[EMAIL PROTECTED]> wrote:

1) To avoid "module explosion", you suggested to put it in e.g. kernel-5 instead of a new lib-5. We can use that approach but now we are limiting
the
usage of the new methods in kernel and/or other modules that has
dependency/access to kernel-5 module. From design perspective, it makes sense to have the 1.5 J2DoPrivHelp functions in lib(-5), that can be
potentially accessed from ANY other modules.

Agreed. In practice, this isn't that big of a problem, since it
happens that all Java 5 modules have a dependency on kernel-5. Either
approach is fine with me.

2) If we put the new 1.5 functions in lib-5, then we need a different
class
e.g. J2DoPriv15Helper.java. This makes an unnatural feel of using the
J2DoPrivHelper.

How is that unnatural?

3) Is there still a need to support 1.4 at all? Is the benefit of
supporting
1.4 justify the source module complexity?

At least one downstream product (Kodo) still supports deploying against
1.4.

-Patrick

On 9/24/07, Albert Lee <[EMAIL PROTECTED]> wrote:
Patrick,

Thanks for spotting the 1.4 compile scenario which I am still new and
ignorance on this topic.

Now that I understand the problem, I have a few observations and
questions:

1) To avoid "module explosion", you suggested to put it in e.g. kernel-5 instead of a new lib-5. We can use that approach but now we are limiting
the
usage of the new methods in kernel and/or other modules that has
dependency/access to kernel-5 module. From design perspective, it makes sense to have the 1.5 J2DoPrivHelp functions in lib(-5), that can be
potentially accessed from ANY other modules.

2) If we put the new 1.5 functions in lib-5, then we need a different
class
e.g. J2DoPriv15Helper.java. This makes an unnatural feel of using the
J2DoPrivHelper.

3) Is there still a need to support 1.4 at all? Is the benefit of
supporting
1.4 justify the source module complexity?

Thanks,
Albert Lee.

On 9/23/07, Patrick Linskey <[EMAIL PROTECTED]> wrote:

Hi,

This change (and r577877, the port of this to the 1.0.x branch)
contains Java 5 code. So, it should be in the a new class in a
1.5-capable module (either a new openjpa-lib-5 dir, or maybe the
openjpa-kernel-5 module, to save on module explosion).

FYI, you can run a compile that detects these types of problems by
running 'mvn compile -Djava14.jar=/path/to/jdk1.4/rt.jar', or
alternately, by submitting your changes through the TeamCity install
that I wrote about earlier. You can see these types of problems as
they occur by monitoring that same TeamCity install.

I could set things up so that key build failures were emailed out to
[email protected] from the TeamCity install.

-Patrick

On 9/18/07, [EMAIL PROTECTED] <[EMAIL PROTECTED]> wrote:
Author: kwsutter
Date: Tue Sep 18 12:44:06 2007
New Revision: 577029

URL: http://svn.apache.org/viewvc?rev=577029&view=rev
Log:
OPENJPA-369.  Committed Albert's changes for the Java 2 Security
updates
for the Solaris platform.

Modified:


openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/ datacache/DataCacheScheduler.java


openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/ util/J2DoPrivHelper.java


openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/ openjpa/persistence/jdbc/AnnotationPersistenceMappingParser.java


openjpa/trunk/openjpa-persistence/src/main/java/org/apache/ openjpa/persistence/AnnotationPersistenceMetaDataParser.java


openjpa/trunk/openjpa-persistence/src/main/java/org/apache/ openjpa/persistence/AnnotationPersistenceXMLMetaDataParser.java


openjpa/trunk/openjpa-persistence/src/main/java/org/apache/ openjpa/persistence/PersistenceMetaDataDefaults.java


openjpa/trunk/openjpa-persistence/src/main/java/org/apache/ openjpa/persistence/PersistenceMetaDataFactory.java

Modified:

openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/ datacache/DataCacheScheduler.java
URL:

http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/ main/java/org/apache/openjpa/datacache/DataCacheScheduler.java? rev=577029&r1=577028&r2=577029&view=diff


==================================================================== ==========
---

openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/ datacache/DataCacheScheduler.java
(original)
+++

openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/ datacache/DataCacheScheduler.java
Tue Sep 18 12:44:06 2007
@@ -99,10 +99,9 @@
         _caches.put(cache, schedule);
         _stop = false;
         if (_thread == null) {
-            _thread = (Thread) AccessController
- .doPrivileged(J2DoPrivHelper.newThreadAction (this,
_loc.get(
-                    "scheduler-name").getMessage()));
-            _thread.setDaemon(true);
+            _thread = (Thread) AccessController.doPrivileged
(J2DoPrivHelper
+                .newDaemonThreadAction(this,
_loc.get("scheduler-name")
+                    .getMessage()));
             _thread.start();
             if (_log.isTraceEnabled())
                 _log.trace(_loc.get("scheduler-start",
_thread.getName()));

Modified:

openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/ util/J2DoPrivHelper.java
URL:

http://svn.apache.org/viewvc/openjpa/trunk/openjpa-lib/src/main/ java/org/apache/openjpa/lib/util/J2DoPrivHelper.java? rev=577029&r1=577028&r2=577029&view=diff


==================================================================== ==========
---

openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/ util/J2DoPrivHelper.java
(original)
+++

openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/ util/J2DoPrivHelper.java
Tue Sep 18 12:44:06 2007
@@ -24,6 +24,7 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.AnnotatedElement;
 import java.net.InetAddress;
 import java.net.MalformedURLException;
 import java.net.ServerSocket;
@@ -48,6 +49,9 @@
  * methods:
  * <ul>
  * <li>AccessibleObject.setAccessible
+ * <li>AnnotatedElement.getAnnotations
+ * <li>AnnotatedElement.getDeclaredAnnotations
+ * <li>AnnotatedElement.isAnnotationPresent
  * <li>Class.forName
  * <li>Class.getClassLoader
  * <li>Class.getDeclaredField
@@ -325,6 +329,60 @@
     }

     /**
+     * Return a PrivilegeAction object for
AnnotatedElement.getAnnotations().
+     *
+     * Requires security policy:
+     *   'permission java.lang.RuntimePermission
"accessDeclaredMembers";'
+     *
+     * @return Annotation[]
+     */
+    public static final PrivilegedAction getAnnotationsAction(
+        final AnnotatedElement element) {
+        return new PrivilegedAction() {
+            public Object run() {
+                return element.getAnnotations();
+            }
+        };
+    }
+
+    /**
+     * Return a PrivilegeAction object for
+     *   AnnotatedElement.getDeclaredAnnotations().
+     *
+     * Requires security policy:
+     *   'permission java.lang.RuntimePermission
"accessDeclaredMembers";'
+     *
+     * @return Annotation[]
+     */
+    public static final PrivilegedAction
getDeclaredAnnotationsAction(
+        final AnnotatedElement element) {
+        return new PrivilegedAction() {
+            public Object run() {
+                return element.getDeclaredAnnotations();
+            }
+        };
+    }
+
+    /**
+     * Return a PrivilegeAction object for
+     *   AnnotatedElement.isAnnotationPresent().
+     *
+     * Requires security policy:
+     *   'permission java.lang.RuntimePermission
"accessDeclaredMembers";'
+     *
+     * @return Boolean
+     */
+ public static final PrivilegedAction isAnnotationPresentAction(
+        final AnnotatedElement element, final Class
annotationClazz) {
+        return new PrivilegedAction() {
+            public Object run() {
+ return element.isAnnotationPresent (annotationClazz)
+                    ? Boolean.TRUE : Boolean.FALSE;
+            }
+        };
+    }
+
+    /**
      * Return a PrivilegedExceptionAction object for
clazz.newInstance
().
      *
      * Requires security policy:
@@ -794,14 +852,17 @@
      *
      * Requires security policy:
* 'permission java.lang.RuntimePermission"modifyThreadGroup";' + * 'permission java.lang.RuntimePermission "modifyThread";'
      *
      * @return Thread
      */
-    public static final PrivilegedAction newThreadAction(
+    public static final PrivilegedAction newDaemonThreadAction(
         final Runnable target, final String name) {
         return new PrivilegedAction() {
             public Object run() {
-                return new Thread(target, name);
+                Thread thread = new Thread(target, name);
+                thread.setDaemon(true);
+                return thread;
             }
         };
     }

Modified:

openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/ openjpa/persistence/jdbc/AnnotationPersistenceMappingParser.java
URL:

http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence- jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/ AnnotationPersistenceMappingParser.java? rev=577029&r1=577028&r2=577029&view=diff


==================================================================== ==========
---

openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/ openjpa/persistence/jdbc/AnnotationPersistenceMappingParser.java
(original)
+++

openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/ openjpa/persistence/jdbc/AnnotationPersistenceMappingParser.java
Tue Sep 18 12:44:06 2007
@@ -21,6 +21,7 @@
 import java.lang.annotation.Annotation;
 import java.lang.reflect.AnnotatedElement;
 import java.lang.reflect.Modifier;
+import java.security.AccessController;
 import java.sql.Types;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -74,6 +75,7 @@
 import org.apache.openjpa.jdbc.schema.Unique;
 import org.apache.openjpa.jdbc.sql.DBDictionary;
 import org.apache.openjpa.lib.log.Log;
+import org.apache.openjpa.lib.util.J2DoPrivHelper;
 import org.apache.openjpa.lib.util.Localizer;
 import org.apache.openjpa.meta.ClassMetaData;
 import org.apache.openjpa.meta.FieldMetaData;
@@ -1058,7 +1060,9 @@

             if (xmlTypeClass != null
&& StringUtils.isEmpty(pcols [i].columnDefinition())
-                && fm.getDeclaredType
().isAnnotationPresent(xmlTypeClass))
{
+                && ((Boolean) AccessController.doPrivileged
(J2DoPrivHelper
+ .isAnnotationPresentAction (fm.getDeclaredType
(),
+                        xmlTypeClass))).booleanValue()) {
                 DBDictionary dict = ((MappingRepository)
getRepository())
                     .getDBDictionary();
                 if (dict.supportsXMLColumn)

Modified:

openjpa/trunk/openjpa-persistence/src/main/java/org/apache/ openjpa/persistence/AnnotationPersistenceMetaDataParser.java
URL:

http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/ src/main/java/org/apache/openjpa/persistence/ AnnotationPersistenceMetaDataParser.java? rev=577029&r1=577028&r2=577029&view=diff


==================================================================== ==========
---

openjpa/trunk/openjpa-persistence/src/main/java/org/apache/ openjpa/persistence/AnnotationPersistenceMetaDataParser.java
(original)
+++

openjpa/trunk/openjpa-persistence/src/main/java/org/apache/ openjpa/persistence/AnnotationPersistenceMetaDataParser.java
Tue Sep 18 12:44:06 2007
@@ -465,9 +465,14 @@
         // check immediately whether the user is using any
annotations,
         // regardless of mode.  this prevents adding non-entity
classes
to
         // repository if we're ignoring these annotations in
mapping
mode
-        if (!_cls.isAnnotationPresent(Entity.class)
-            && !_cls.isAnnotationPresent(Embeddable.class)
- && !_cls.isAnnotationPresent (MappedSuperclass.class))
+        if (!((Boolean) AccessController.doPrivileged
(J2DoPrivHelper
+            .isAnnotationPresentAction(_cls, Entity.class
))).booleanValue()
+            && !((Boolean) AccessController.doPrivileged
(J2DoPrivHelper
+ .isAnnotationPresentAction(_cls, Embeddable.class
)))
+                .booleanValue()
+            && !((Boolean) AccessController.doPrivileged
(J2DoPrivHelper
+                .isAnnotationPresentAction(_cls,
MappedSuperclass.class
)))
+                .booleanValue())
             return null;

         // find / create metadata
@@ -762,7 +767,9 @@
                 J2DoPrivHelper.getDeclaredFieldsAction(
                     meta.getDescribedType()));
             for (int i = 0; i < fields.length; i++)
-                if (fields[i].isAnnotationPresent(
DetachedState.class))
+                if (((Boolean) AccessController.doPrivileged
(J2DoPrivHelper
+                    .isAnnotationPresentAction(fields[i],
DetachedState.class)))
+                    .booleanValue())
                     meta.setDetachedState(fields[i].getName());
         }
     }
@@ -818,7 +825,8 @@

         MetaDataDefaults def = repos.getMetaDataFactory
().getDefaults();
         for (Method m : methods) {
-            for (Annotation anno : m.getDeclaredAnnotations()) {
+ for (Annotation anno : (Annotation[]) AccessController
+                .doPrivileged(
J2DoPrivHelper.getDeclaredAnnotationsAction(m))) {
MetaDataTag tag = _tags.get (anno.annotationType());
                 if (tag == null)
                     continue;
@@ -956,7 +964,8 @@
         fmd.setExplicit(true);

         AnnotatedElement el = (AnnotatedElement) member;
-        boolean lob = el.isAnnotationPresent(Lob.class);
+        boolean lob = ((Boolean) AccessController.doPrivileged
(J2DoPrivHelper
+            .isAnnotationPresentAction(el, Lob.class
))).booleanValue();
         if (isMetaDataMode()) {
             switch (pstrat) {
                 case BASIC:

Modified:

openjpa/trunk/openjpa-persistence/src/main/java/org/apache/ openjpa/persistence/AnnotationPersistenceXMLMetaDataParser.java
URL:

http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/ src/main/java/org/apache/openjpa/persistence/ AnnotationPersistenceXMLMetaDataParser.java? rev=577029&r1=577028&r2=577029&view=diff


==================================================================== ==========
---

openjpa/trunk/openjpa-persistence/src/main/java/org/apache/ openjpa/persistence/AnnotationPersistenceXMLMetaDataParser.java
(original)
+++

openjpa/trunk/openjpa-persistence/src/main/java/org/apache/ openjpa/persistence/AnnotationPersistenceXMLMetaDataParser.java
Tue Sep 18 12:44:06 2007
@@ -22,10 +22,12 @@
 import java.lang.reflect.Field;
 import java.lang.reflect.Member;
 import java.lang.reflect.Method;
+import java.security.AccessController;

 import org.apache.commons.lang.StringUtils;
 import org.apache.openjpa.conf.OpenJPAConfiguration;
 import org.apache.openjpa.lib.log.Log;
+import org.apache.openjpa.lib.util.J2DoPrivHelper;
 import org.apache.openjpa.lib.util.Localizer;
 import org.apache.openjpa.meta.DelegatingMetaDataFactory;
 import org.apache.openjpa.meta.FieldMetaData;
@@ -171,8 +173,11 @@
     private XMLMetaData parseXMLClassAnnotations() {
         // check immediately whether the class has JAXB XML
annotations
         if (_cls == null || xmlTypeClass == null
-            || !(_cls.isAnnotationPresent(xmlTypeClass)
- && _cls.isAnnotationPresent (xmlRootElementClass)))
+            || !(((Boolean) AccessController.doPrivileged
(J2DoPrivHelper
+                .isAnnotationPresentAction(_cls,
xmlTypeClass))).booleanValue()
+                && ((Boolean) AccessController
+                .doPrivileged(
J2DoPrivHelper.isAnnotationPresentAction
(_cls,
+                    xmlRootElementClass))).booleanValue()))
             return null;

         // find / create metadata
@@ -220,7 +225,9 @@
         Class superclass = cls.getSuperclass();

         // handle inheritance at sub-element level
-        if (superclass.isAnnotationPresent(xmlTypeClass))
+ if (((Boolean) AccessController.doPrivileged (J2DoPrivHelper + .isAnnotationPresentAction(superclass, xmlTypeClass)))
+            .booleanValue())
             populateFromReflection(superclass, meta);

         try {
@@ -240,8 +247,9 @@
                     // avoid JAXB XML bind default name
if (StringUtils.equals (XMLMetaData.defaultName,
xmlname))
                         xmlname = member.getName();
-                    if (((Field) member).getType()
-                        .isAnnotationPresent(xmlTypeClass)) {
+                    if (((Boolean) AccessController.doPrivileged
(J2DoPrivHelper
+                        .isAnnotationPresentAction(((Field)
member).getType(),
+                            xmlTypeClass))).booleanValue()) {
                         field = _repos.addXMLMetaData(((Field)
member).getType()
                             , member.getName());
                         parseXmlRootElement(((Field)
member).getType(),
field);

Modified:

openjpa/trunk/openjpa-persistence/src/main/java/org/apache/ openjpa/persistence/PersistenceMetaDataDefaults.java
URL:

http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/ src/main/java/org/apache/openjpa/persistence/ PersistenceMetaDataDefaults.java? rev=577029&r1=577028&r2=577029&view=diff


==================================================================== ==========
---

openjpa/trunk/openjpa-persistence/src/main/java/org/apache/ openjpa/persistence/PersistenceMetaDataDefaults.java
(original)
+++

openjpa/trunk/openjpa-persistence/src/main/java/org/apache/ openjpa/persistence/PersistenceMetaDataDefaults.java
Tue Sep 18 12:44:06 2007
@@ -116,7 +116,8 @@
         if (member == null)
             return null;
         AnnotatedElement el = (AnnotatedElement) member;
-        if (el.isAnnotationPresent(Transient.class))
+ if (((Boolean) AccessController.doPrivileged (J2DoPrivHelper
+            .isAnnotationPresentAction(el, Transient.class
))).booleanValue())
             return TRANSIENT;
         if (fmd != null
             && fmd.getManagement() !=
FieldMetaData.MANAGE_PERSISTENT)
@@ -182,7 +183,8 @@
         }

         //### EJB3: what if defined in XML?
-        if (type.isAnnotationPresent(Embeddable.class))
+ if (((Boolean) AccessController.doPrivileged (J2DoPrivHelper
+            .isAnnotationPresentAction(type, Embeddable.class
))).booleanValue())
             return EMBEDDED;
         if (Serializable.class.isAssignableFrom(type))
             return BASIC;
@@ -271,7 +273,8 @@
         Annotation[] annos;
         String name;
         for (int i = 0; i < members.length; i++) {
-            annos = members[i].getAnnotations();
+            annos = (Annotation[]) AccessController.doPrivileged
(J2DoPrivHelper
+                .getAnnotationsAction(members[i]));
             for (int j = 0; j < annos.length; j++) {
                 name = annos[j].annotationType().getName();
                 if ((name.startsWith("javax.persistence.")
@@ -317,7 +320,9 @@

     private boolean isAnnotatedTransient(Member member) {
         return member instanceof AnnotatedElement
-            && ((AnnotatedElement) member).isAnnotationPresent(
Transient.class);
+            && ((Boolean) AccessController.doPrivileged
(J2DoPrivHelper
+                .isAnnotationPresentAction(((AnnotatedElement)
member),
+                    Transient.class))).booleanValue();
     }

     private void logNoSetter(ClassMetaData meta, String name,
Exception
e) {

Modified:

openjpa/trunk/openjpa-persistence/src/main/java/org/apache/ openjpa/persistence/PersistenceMetaDataFactory.java
URL:

http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/ src/main/java/org/apache/openjpa/persistence/ PersistenceMetaDataFactory.java? rev=577029&r1=577028&r2=577029&view=diff


==================================================================== ==========
---

openjpa/trunk/openjpa-persistence/src/main/java/org/apache/ openjpa/persistence/PersistenceMetaDataFactory.java
(original)
+++

openjpa/trunk/openjpa-persistence/src/main/java/org/apache/ openjpa/persistence/PersistenceMetaDataFactory.java
Tue Sep 18 12:44:06 2007
@@ -293,18 +293,26 @@
             return null;
         Collection classes = repos.loadPersistentTypes(false,
loader);
         for (Class cls : (Collection<Class>) classes) {
-            if (cls.isAnnotationPresent(NamedQuery.class) &&
hasNamedQuery
+            if (((Boolean) AccessController.doPrivileged
(J2DoPrivHelper
+ .isAnnotationPresentAction(cls, NamedQuery.class)))
+                .booleanValue() && hasNamedQuery
                 (queryName, (NamedQuery) cls.getAnnotation(
NamedQuery.class)))
                 return cls;
-            if (cls.isAnnotationPresent(NamedQueries.class) &&
+            if (((Boolean) AccessController.doPrivileged
(J2DoPrivHelper
+ .isAnnotationPresentAction(cls, NamedQueries.class
)))
+                .booleanValue() &&
                 hasNamedQuery(queryName, ((NamedQueries) cls.
                     getAnnotation(NamedQueries.class)).value()))
                 return cls;
- if (cls.isAnnotationPresent (NamedNativeQuery.class) &&
+            if (((Boolean) AccessController.doPrivileged
(J2DoPrivHelper
+                .isAnnotationPresentAction(cls,
NamedNativeQuery.class
)))
+                .booleanValue() &&
hasNamedNativeQuery(queryName, (NamedNativeQuery)
cls.
                     getAnnotation(NamedNativeQuery.class)))
                 return cls;
- if (cls.isAnnotationPresent (NamedNativeQueries.class)
&&
+            if (((Boolean) AccessController.doPrivileged
(J2DoPrivHelper
+                .isAnnotationPresentAction(cls,
NamedNativeQueries.class)))
+                .booleanValue() &&
                 hasNamedNativeQuery(queryName,
((NamedNativeQueries)
cls.
                     getAnnotation(NamedNativeQueries.class
)).value()))
                 return cls;
@@ -320,13 +328,17 @@

         Collection classes = repos.loadPersistentTypes(false,
loader);
         for (Class cls : (Collection<Class>) classes) {
-
- if (cls.isAnnotationPresent (SqlResultSetMapping.class)
&&
+
+            if (((Boolean) AccessController.doPrivileged
(J2DoPrivHelper
+                .isAnnotationPresentAction(cls,
SqlResultSetMapping.class)))
+                .booleanValue() &&
hasRSMapping(rsMappingName, (SqlResultSetMapping)
cls.
                 getAnnotation(SqlResultSetMapping.class)))
                 return cls;
-
- if (cls.isAnnotationPresent (SqlResultSetMappings.class)
&&
+
+            if (((Boolean) AccessController.doPrivileged
(J2DoPrivHelper
+                .isAnnotationPresentAction(cls,
SqlResultSetMappings.class)))
+                .booleanValue() &&
hasRSMapping(rsMappingName, ((SqlResultSetMappings)
cls.
                 getAnnotation(SqlResultSetMappings.class
)).value()))
                 return cls;





--
Patrick Linskey
202 669 5907




--
Patrick Linskey
202 669 5907




--
Patrick Linskey
202 669 5907

Craig Russell
Architect, Sun Java Enterprise System http://java.sun.com/products/jdo
408 276-5638 mailto:[EMAIL PROTECTED]
P.S. A good JDO? O, Gasp!

Attachment: smime.p7s
Description: S/MIME cryptographic signature

Reply via email to