Uploaded image for project: 'Confluence Data Center'
  1. Confluence Data Center
  2. CONFSERVER-37166

DefaultContentPropertyManager.transferProperties does not respect "text" type of content property if length < 255

    XMLWordPrintable

Details

    Description

      Summary

      Confluence do have a generic storage system ('Bucket') for plugins to stored their data of different types (also known as properties) in Confluence DB, specifically in the os_propertyentry table. When a plugin uses it (e.g. Scaffolding or Tracking plugin), it will then add a specific type and a key it needs into this table, to be referred to later on.

      In its implementation, Confluence do have under-the-hood optimisation in storing such properties. This optimisation is being called by the DefaultContentPropertyManager.transferProperties() method and is only being called by Confluence when a private draft is promoted to the page (when Collaborative Editing disabled).
      The optimisation process itself includes the following:

      • Should the macro's metadata consists of a String with lengths <= 255 characters, then the metadata datatype then will be changed to String and as such the metadata will then be added to the string_val column.
      • Otherwise, if the macro's metadata consists of a String with lengths > 255 characters, then the metadata datatype then will be changed to Text and as such the metadata will then be added to the text_val column.
      DefaultContentPropertyManager.java
          @Override
          public void transferProperties(ContentEntityObject source, ContentEntityObject destination) {
              PropertySet sourceProperties = getPropertySet(source);
              PropertySet destProperties = getPropertySet(destination);
      
              for (Object o : sourceProperties.getKeys()) {
                  String key = (String) o;
                  Object value = sourceProperties.getAsActualType(key);
      
                  destProperties.setAsActualType(key, value);
                  cacheSet(destination, key, (Serializable) value);
      
                  sourceProperties.remove(key);
                  cacheRemove(source, key);
              }
          }
      
      PropertySet.java
        public void setAsActualType(String key, Object value) throws PropertyException {
          if(value instanceof Boolean) {
            setBoolean(key, ((Boolean)value));
          } else if(value instanceof Integer) {
            setInt(key, (Integer)value);
          } else if(value instanceof Long) {
            setLong(key, (Long)value);
          } else if(value instanceof Double) {
            setDouble(key, (Double)value);
          } else if(value instanceof String) {
            if(value.toString().length() > 255) {
              setText(key, (String)value);
            } else {
              setString(key, (String)value);
            }
          } else if(value instanceof Date) {
            setDate(key, (Date)value);
          } else {
            setObject(key, value);
          }
        }
      

      The catch here is that if the macro metadata consists of a String with lengths <= 255, the metadata datatype then will be changed to String and as such the metadata will then be added to the string_val column instead of text_val column.

      That means that properties that are set on draft version via ContentPropertyManager.setTextProperty and are shorter than 255 characters will throw an exception when later accessed with ContentPropertyManager.getTextProperty.
      This is what we see in the two scenarios below - when the plugin is trying to retrieve the data from text_val column, it then won't able to find the correct type - 6 and we'd then be seeing some errors from either the logs or Confluence UI.

      Scenario1 : Tracking Plugin

      Steps to reproduce

      • Install tracking plugin in version 3.1.5
      • click "create", select "blank page"
      • DO NOT save yet
      • add an image
      • preview the draft
        • (optional) check the database
          select * from OS_PROPERTYENTRY where entity_id = <CONTENT_ENTITY_ID_OF_DRAFT>
          

          (it should show three entries with key_type=6)

      • save the page, thereby creating a "real page" out of the draft you were editing
        • check the database again
          select * from OS_PROPERTYENTRY where entity_id = <CONTENT_ENTITY_ID_OF_PAGE>
          

          (it should show the three entries it showed before, but now with key_type=5)

      • view the page and/or the aforementioned image with a different user
      • check the logs (see attached atlassian-confluence.log for an example

      Expected Results

      The previously added image is rendered as per expected.

      Actual Results

      The following error is thrown in <Confluence-Home>/logs/atlassian-confluence.log:

      2015-04-08 16:02:05,960 ERROR [http-bio-127.0.0.1-8180-exec-29 StartTime "2015-04-08 16:02:05.955" RequestURI "/download/attachments/271745409/TunnelblickAuthIcon.png" QueryString "version=1&modificationDate=1428500697000&api=v2" User ""] [atlassian.event.internal.AsynchronousAbleEventDispatcher] run There was an exception thrown trying to dispatch event [com.atlassian.confluence.event.events.content.attachment.AttachmentViewEvent[source=com.atlassian.confluence.servlet.download.AttachmentDownload@6a81c5c0]] from the invoker [SingleParameterMethodListenerInvoker{method=public void net.customware.confluence.plugin.tracking.TrackingEventListener.onEvent(com.atlassian.confluence.event.events.content.attachment.AttachmentViewEvent), listener=net.customware.confluence.plugin.tracking.TrackingEventListener@59caa1c1}]
       -- url: /download/attachments/271745409/TunnelblickAuthIcon.png | userName: | referer: 
      java.lang.RuntimeException: Existing key 'tracking.attributes.271778313.viewCount' does not have matching type of 6
      	at com.atlassian.event.internal.SingleParameterMethodListenerInvoker.invoke(SingleParameterMethodListenerInvoker.java:54)
      	at com.atlassian.event.internal.AsynchronousAbleEventDispatcher$1$1.run(AsynchronousAbleEventDispatcher.java:48)
      

      Scenario2 : Scaffolding Plugin

      Steps to reproduce

      1. Install 'Scaffolding' plugin from Service Rocket and make sure that Collaborative Editing is turned off in Confluence.
      2. Create new page 'Page A' with 'Excerpt-Data Macro' macro named 'Macro A' and save the page.
      3. Click 'Edit Contents', Scaffolding window will open up, and fill 'Hello World' in the text field.
      4. Save the changes.
      5. Check your os_propertyentry table - you should see a row with a non-empty <metadata> tag in text_val field and key_type value set as 6.
      6. Access the page we created in Step 2 again and click 'Copy with Scaffolding XML' from the menu to copy this page, then save the 'Page B' page.
        The number on the entity_key value (e.g. ~metadata.1) is referring to the page's version number. Thus, to replicate this issue (affecting records with ~metadata.1 as the entity_key value), we'd then need to make use of the Copy with Scaffolding XML feature.
      7. Check your os_propertyentry table - you should see a row with <metadata> tag in string_val field and key_type value set as 5.

      Expected Results

      The 'Excerpt-Data Macro' to be rendered as per expected.

      Actual Results

      The 'Excerpt-Data Macro' will not be rendered and instead we're seeing the following error:

      Error rendering macro 'excerpt-data' key '~metadata.1' does not have matching type of 6

      You may also be seeing the following error is thrown in <Confluence-Home>/logs/atlassian-confluence.log:

      2019-03-13 15:49:34,461 ERROR [Caesium-1-1] [atlassian.labs.lucenecompat.Extractor2ToExtractorAdapter] addFields Error extracting search fields from page: Test page v.6 (123456) using net.customware.confluence.plugin.scaffolding.indexing.MetadataStorageIndexer@7da19cea: key '~metadata.1' does not have matching type of 6
      com.opensymphony.module.propertyset.PropertyException: key '~metadata.1' does not have matching type of 6
      	at bucket.user.propertyset.BucketPropertySet.get(BucketPropertySet.java:163)
      	at com.opensymphony.module.propertyset.AbstractPropertySet.getText(AbstractPropertySet.java:385)
      	at com.atlassian.confluence.core.DefaultContentPropertyManager.retrieveAndCacheProperty(DefaultContentPropertyManager.java:217)
      	at com.atlassian.confluence.core.DefaultContentPropertyManager.cacheGet(DefaultContentPropertyManager.java:189)
      	at com.atlassian.confluence.core.DefaultContentPropertyManager.getTextProperty(DefaultContentPropertyManager.java:103)
      ...
      	at com.sun.proxy.$Proxy518.getTextProperty(Unknown Source)
      	at com.servicerocket.confluence.randombits.metadata.impl.DefaultMetadataManager.getProperty(DefaultMetadataManager.java:149)
      	at com.servicerocket.confluence.randombits.metadata.impl.DefaultMetadataManager.loadDataXML(DefaultMetadataManager.java:322)
      	at com.servicerocket.confluence.randombits.metadata.impl.DefaultMetadataManager.loadDataMap(DefaultMetadataManager.java:293)
      	at com.servicerocket.confluence.randombits.metadata.impl.DefaultMetadataManager.loadNewWritableData(DefaultMetadataManager.java:276)
      	at com.servicerocket.confluence.randombits.metadata.impl.DefaultMetadataManager.loadWritableData(DefaultMetadataManager.java:260)
      ...
      	at com.sun.proxy.$Proxy2449.loadWritableData(Unknown Source)
      	at net.customware.confluence.plugin.scaffolding.indexing.MetadataStorageIndexer.getMetadataStorage(MetadataStorageIndexer.java:126)
      	at net.customware.confluence.plugin.scaffolding.indexing.MetadataStorageIndexer.extractFields(MetadataStorageIndexer.java:96)
      ...
      

      Attachments

        Issue Links

          Activity

            People

              hrehioui Hasnae (Inactive)
              3bea9b9e5330 Martin Sander
              Votes:
              18 Vote for this issue
              Watchers:
              35 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved: