• We collect Confluence feedback from various sources, and we evaluate what we've collected when planning our product roadmap. To understand how this piece of feedback will be reviewed, see our Implementation of New Features Policy.

      Dear Confluence team  

      As part of preparing our apps to the new XStream changes planned to be introduced by Confluence 7.10, we are facing one issue while doing the marshalling/unmarshalling process using the XStreamManagerCompat component.

       

      We're saving in Bandana an object containing a list of Label (com.atlassian.confluence.labels.Label). While doing toXml() directly with XStreamManagerCompat bandana we get this exception:

      this:

      String marshalledXml = xStreamManagerCompat.toXML(ourObjectContainingLabel);
      

      causes this:

      2020-12-09 10:48:01,241 ERROR [Caesium-1-1] [confluence.remotepublishing.macro.RemotePublishPageMacro] doExecute There was an error while publishing content2020-12-09 10:48:01,241 ERROR [Caesium-1-1] [confluence.remotepublishing.macro.RemotePublishPageMacro] doExecute There was an error while publishing contentcom.thoughtworks.xstream.converters.reflection.ObjectAccessException: Invalid final field com.atlassian.confluence.labels.Namespace.namespacePrefix at com.thoughtworks.xstream.converters.reflection.PureJavaReflectionProvider.validateFieldAccess(PureJavaReflectionProvider.java:150) at com.thoughtworks.xstream.converters.reflection.PureJavaReflectionProvider.visitSerializableFields(PureJavaReflectionProvider.java:105) at com.thoughtworks.xstream.converters.reflection.ReflectionConverter.marshal(ReflectionConverter.java:44) at com.thoughtworks.xstream.core.ReferenceByXPathMarshaller.convertAnother(ReferenceByXPathMarshaller.java:36) at com.thoughtworks.xstream.converters.reflection.ReflectionConverter$1.writeField(ReflectionConverter.java:78) at com.thoughtworks.xstream.converters.reflection.ReflectionConverter$1.visit(ReflectionConverter.java:59) at com.thoughtworks.xstream.converters.reflection.PureJavaReflectionProvider.visitSerializableFields(PureJavaReflectionProvider.java:114) at com.thoughtworks.xstream.converters.reflection.ReflectionConverter.marshal(ReflectionConverter.java:44) at com.thoughtworks.xstream.core.ReferenceByXPathMarshaller.convertAnother(ReferenceByXPathMarshaller.java:36) at com.thoughtworks.xstream.converters.collections.AbstractCollectionConverter.writeItem(AbstractCollectionConverter.java:68) at com.thoughtworks.xstream.converters.collections.CollectionConverter.marshal(CollectionConverter.java:47) at com.thoughtworks.xstream.core.ReferenceByXPathMarshaller.convertAnother(ReferenceByXPathMarshaller.java:36) at com.thoughtworks.xstream.converters.reflection.ReflectionConverter$1.writeField(ReflectionConverter.java:78) at com.thoughtworks.xstream.converters.reflection.ReflectionConverter$1.visit(ReflectionConverter.java:59) at com.thoughtworks.xstream.converters.reflection.PureJavaReflectionProvider.visitSerializableFields(PureJavaReflectionProvider.java:114) at com.thoughtworks.xstream.converters.reflection.ReflectionConverter.marshal(ReflectionConverter.java:44) at com.thoughtworks.xstream.core.ReferenceByXPathMarshaller.convertAnother(ReferenceByXPathMarshaller.java:36) at com.thoughtworks.xstream.converters.reflection.ReflectionConverter$1.writeField(ReflectionConverter.java:78) at com.thoughtworks.xstream.converters.reflection.ReflectionConverter$1.visit(ReflectionConverter.java:59) at com.thoughtworks.xstream.converters.reflection.PureJavaReflectionProvider.visitSerializableFields(PureJavaReflectionProvider.java:114) at com.thoughtworks.xstream.converters.reflection.ReflectionConverter.marshal(ReflectionConverter.java:44) at com.thoughtworks.xstream.core.ReferenceByXPathMarshaller.convertAnother(ReferenceByXPathMarshaller.java:36) at com.thoughtworks.xstream.converters.collections.AbstractCollectionConverter.writeItem(AbstractCollectionConverter.java:68) at com.thoughtworks.xstream.converters.collections.CollectionConverter.marshal(CollectionConverter.java:47) at com.thoughtworks.xstream.core.ReferenceByXPathMarshaller.convertAnother(ReferenceByXPathMarshaller.java:36) at com.thoughtworks.xstream.converters.collections.AbstractCollectionConverter.writeItem(AbstractCollectionConverter.java:68) at com.thoughtworks.xstream.converters.collections.MapConverter.marshal(MapConverter.java:51) at com.thoughtworks.xstream.core.ReferenceByXPathMarshaller.convertAnother(ReferenceByXPathMarshaller.java:36) at com.thoughtworks.xstream.core.TreeMarshaller.start(TreeMarshaller.java:46) at com.thoughtworks.xstream.core.ReferenceByXPathMarshallingStrategy.marshal(ReferenceByXPathMarshallingStrategy.java:17) at com.thoughtworks.xstream.XStream.marshal(XStream.java:489) at com.thoughtworks.xstream.XStream.marshal(XStream.java:479) at com.thoughtworks.xstream.XStream.toXML(XStream.java:464) at com.atlassian.confluence.compat.setup.xstream.XStream111Compat.toXML(XStream111Compat.java:20) at com.atlassian.confluence.compat.setup.xstream.XStreamManagerCompat.toXML(XStreamManagerCompat.java:50) at com.comalatech.confluence.remotepublishing.confluence.BandanaPublishHistoryManager.saveBandanaPublishHistoryInfoSpaceConfigs(BandanaPublishHistoryManager.java:76)

      As you can see, the problem is related while the library handles the final attribute Namespace of the Label. The error is the same if we don't call to the XStreamManagerCompat and instead we rely in the XML marshalling of BandanaManager{{.}}

       

      Previous to this, we're using directly a XStream instance defining the StaxDriver and a  custom converter to be able to get rid of this exception and marshall/unmarshall the label object correctly. Code sample:

      XStream xStream = new XStream(new StaxDriver());
      // We were having problems with Label's Namespace, as it has a private constructor, so we need to convert from object to
      // XML and from XML to object manually
      xStream.registerConverter(new Converter() {
          @Override
          public boolean canConvert(Class aClass) {
             return aClass.equals(Namespace.class);
          }
          @Override
          public void marshal(Object o, HierarchicalStreamWriter hierarchicalStreamWriter, MarshallingContext marshallingContext) {
             Namespace namespace = (Namespace)o;
             hierarchicalStreamWriter.startNode("namespacePrefix");
             hierarchicalStreamWriter.setValue(namespace.getPrefix());
             hierarchicalStreamWriter.endNode();
          }
          @Override
          public Object unmarshal(HierarchicalStreamReader hierarchicalStreamReader, UnmarshallingContext unmarshallingContext) {
              hierarchicalStreamReader.moveDown();
              String namespacePrefix = hierarchicalStreamReader.getValue();
              hierarchicalStreamReader.moveUp();
              if (namespacePrefix.equals("my")){
                return Namespace.PERSONAL;
              }else if (namespacePrefix.equals("team")){
                return Namespace.TEAM;
              }else if (namespacePrefix.equals("global")){
                return Namespace.GLOBAL;
              }else {
                return Namespace.SYSTEM;
              }
           }
      });
      

      So we're kind of stuck here if from Confluence 7.10 we cannot add this kind of custom converters or configurations using the XStreamManagerCompat.

      • Are you plan to resolve this adding some way to customize the internal XStream?
      • Or in this case as the error is related to your internal Label model, maybe you can provide XStreamManagerCompat implementation already being compatible to marshall/unmarshall Confluence model.

       

      Let us know if you need anything more.

       

            [CONFSERVER-60576] Confluence 7.10 XStream Compat while saving

            Sen Geronimo made changes -
            Workflow Original: JAC Suggestion Workflow 4 [ 4035422 ] New: JAC Suggestion Workflow 3 [ 4338616 ]
            Ganesh Gautam made changes -
            Remote Link New: This issue links to "Page (Confluence)" [ 632862 ]
            Adilson Carvalho (Inactive) made changes -
            Remote Link New: This issue links to "Page (Confluence)" [ 568481 ]
            Adilson Carvalho (Inactive) made changes -
            Remote Link New: This issue links to "Page (Confluence)" [ 568582 ]

            Hi @Ganesh Gautam,

            Thank you for your reply. I have created a separate suggestion as per your recommendation CONFSERVER-60867.

            Regards,

            Ahmed

            Ahmed Alaghbari added a comment - Hi @Ganesh Gautam, Thank you for your reply. I have created a separate suggestion as per your recommendation  CONFSERVER-60867 . Regards, Ahmed
            Ganesh Gautam made changes -
            Remote Link New: This issue links to "Page (Confluence)" [ 523444 ]

            Hi ahmed.alaghbari

            Thanks for using the new API and giving this amazing feedback. I am happy to see your app already taking advantage of our backward compatibility layer on new XStream.

            Lastly, I would recommend you to raise a separate suggestion, should you need API for mapper in future.

            Regards,

            Ganesh

            Ganesh Gautam added a comment - Hi ahmed.alaghbari Thanks for using the new API and giving this amazing feedback. I am happy to see your app already taking advantage of our backward compatibility layer on new XStream. Lastly, I would recommend you to raise a separate suggestion, should you need API for mapper in future. Regards, Ganesh

            Hi,

            First of all I'd like to thank @David Herrera Alonso for requesting the additional compatibility methods and for presenting the arguments for them well. Also, I'd like to thank @Ganesh Gautam for his timely response to the feedback and for making the suggested improvements available with the release of Confluence 7.10.0. Both your efforts have made it possible to make a much smoother transition into the new XStream version while still maintaining compatibility for those users still on the older version.

            With that being said, I would like to also propose further extension to confluence-compat-lib to cover the non-backward compatible XStream#getMapper method. To give you a bit of context, we are using the new registerConverter method. However, our custom Converter extends from XStream's MapConverter whose constructor requires a com.thoughtworks.xstream.mapper.Mapper. This was easily obtained by running XStream#getClassMapper on XStream 1.1.1 but since this was renamed on XStream1.4.14 to XStream#getMapper, we have resorted to using reflections to cater for both cases. It would be great if the compatibility library could expose this method to enable us to register convertors more efficiently and rely completely on XStreamManagerCompat ensuring better future proofing.

            @Component
            public class XStreamManagerCompat {
            ...
            /**
            * Retrieve the Mapper.
            *
            @return the default Mapper in XStream 1.4.x or the default ClassMapper in XStream 1.1.1.
            */
                public Mapper getMapper() {
                    delegate.getMapper();
            }
            }

             
            Thanks again for your help in making this transition as seamless as possible and for considering the suggestion brought forward in this comment.

            Ahmed.

            Ahmed Alaghbari added a comment - Hi, First of all I'd like to thank @David Herrera Alonso for requesting the additional compatibility methods and for presenting the arguments for them well. Also, I'd like to thank @Ganesh Gautam for his timely response to the feedback and for making the suggested improvements available with the release of Confluence 7.10.0. Both your efforts have made it possible to make a much smoother transition into the new XStream version while still maintaining compatibility for those users still on the older version. With that being said, I would like to also propose further extension to confluence-compat-lib to cover the non-backward compatible XStream#getMapper  method. To give you a bit of context, we are using the new registerConverter method. However, our custom Converter extends from XStream's MapConverter whose constructor requires a com.thoughtworks.xstream.mapper.Mapper . This was easily obtained by running XStream#getClassMapper on XStream 1.1.1 but since this was renamed on XStream1.4.14 to XStream#getMapper , we have resorted to using reflections to cater for both cases. It would be great if the compatibility library could expose this method to enable us to register convertors more efficiently and rely completely on XStreamManagerCompat  ensuring better future proofing. @Component public class XStreamManagerCompat { ... /** * Retrieve the Mapper. * *  @return the default Mapper in XStream 1.4.x or the default ClassMapper in XStream 1.1.1. */     public Mapper getMapper() {         delegate.getMapper(); } }   Thanks again for your help in making this transition as seamless as possible and for considering the suggestion brought forward in this comment. Ahmed.
            Brendan McNamara made changes -
            Resolution New: Fixed [ 1 ]
            Status Original: Waiting for Release [ 12075 ] New: Closed [ 6 ]

            7.10.0 release has shipped with this change.

            Brendan McNamara added a comment - 7.10.0 release has shipped with this change.

              ggautam Ganesh Gautam
              16fa4887a4db David Herrera Alonso
              Votes:
              1 Vote for this issue
              Watchers:
              6 Start watching this issue

                Created:
                Updated:
                Resolved: