You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ro...@apache.org on 2017/10/18 23:24:41 UTC

[sling-org-apache-sling-hapi] 25/27: SLING-6978 [HApi] NPE if type is fetched from cache before the end of initialization

This is an automated email from the ASF dual-hosted git repository.

rombert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-hapi.git

commit 1f510cec25134676b32cbe6a45a268dc969f2e71
Author: Andrei Dulvac <du...@apache.org>
AuthorDate: Thu Jun 29 12:51:24 2017 +0000

    SLING-6978 [HApi] NPE if type is fetched from cache before the end of initialization
    
    git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1800275 13f79535-47bb-0310-9956-ffa450edef68
---
 src/main/java/org/apache/sling/hapi/HApiType.java  |   1 +
 .../org/apache/sling/hapi/impl/HApiTypeImpl.java   |   5 +-
 .../sling/hapi/impl/HApiTypeLazyWrapper.java       | 188 +++++++++++++++++++++
 .../org/apache/sling/hapi/impl/HApiUtilImpl.java   | 154 +++++++++--------
 4 files changed, 276 insertions(+), 72 deletions(-)

diff --git a/src/main/java/org/apache/sling/hapi/HApiType.java b/src/main/java/org/apache/sling/hapi/HApiType.java
index 0851f92..8fcdfac 100644
--- a/src/main/java/org/apache/sling/hapi/HApiType.java
+++ b/src/main/java/org/apache/sling/hapi/HApiType.java
@@ -27,6 +27,7 @@ import java.util.Map;
 /**
  * A Hypermedia API type.
  */
+
 @ProviderType
 public interface HApiType {
 
diff --git a/src/main/java/org/apache/sling/hapi/impl/HApiTypeImpl.java b/src/main/java/org/apache/sling/hapi/impl/HApiTypeImpl.java
index 516229b..8d6eb2c 100644
--- a/src/main/java/org/apache/sling/hapi/impl/HApiTypeImpl.java
+++ b/src/main/java/org/apache/sling/hapi/impl/HApiTypeImpl.java
@@ -23,7 +23,10 @@ import org.apache.sling.hapi.HApiType;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.util.*;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
 
 /**
  * {@inheritDoc}
diff --git a/src/main/java/org/apache/sling/hapi/impl/HApiTypeLazyWrapper.java b/src/main/java/org/apache/sling/hapi/impl/HApiTypeLazyWrapper.java
new file mode 100644
index 0000000..62e1fe6
--- /dev/null
+++ b/src/main/java/org/apache/sling/hapi/impl/HApiTypeLazyWrapper.java
@@ -0,0 +1,188 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ******************************************************************************/
+package org.apache.sling.hapi.impl;
+
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.hapi.HApiException;
+import org.apache.sling.hapi.HApiProperty;
+import org.apache.sling.hapi.HApiType;
+import org.apache.sling.hapi.HApiUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jcr.RepositoryException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * {@inheritDoc}
+ */
+public class HApiTypeLazyWrapper implements HApiType {
+    public static final Logger LOG = LoggerFactory.getLogger(HApiTypeLazyWrapper.class);
+    private final HApiUtil util;
+    private final ResourceResolver resolver;
+    private String serverUrl;
+    private final Resource resource;
+
+    /**
+     * A new HApiType that is just a weak reference by name
+     * @param util
+     * @param resolver
+     * @param name
+     */
+    public HApiTypeLazyWrapper(HApiUtil util, ResourceResolver resolver, String serverUrl, String name) {
+        this.util = util;
+        this.resolver = resolver;
+        this.serverUrl = serverUrl;
+        try {
+            this.resource = util.getTypeResource(resolver, name);
+        } catch (RepositoryException e) {
+            throw new HApiException("Can't find type " + name + "!", e);
+        }
+    }
+
+    public HApiTypeLazyWrapper(HApiUtil util, ResourceResolver resolver, String serverUrl, Resource resource) {
+        this.util = util;
+        this.resolver = resolver;
+        this.serverUrl = serverUrl;
+        this.resource = resource;
+    }
+    /**
+     * Load the type from the cache
+     * @return
+     */
+    private HApiType getTypeFromCache() {
+        try {
+            return TypesCache.getInstance(this.util).getType(resolver, resource);
+        } catch (RepositoryException e) {
+            String name = (null != resource) ? resource.getName() : "";
+            throw new HApiException("Can't find type " + name + "!", e);
+        }
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String getName() {
+        return getTypeFromCache().getName();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String getDescription() {
+        return getTypeFromCache().getDescription();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String getPath() {
+        return getTypeFromCache().getPath();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String getUrl() {
+        return this.serverUrl + getPath() + ".html";
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String getFqdn() {
+        return getTypeFromCache().getFqdn();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public List<String> getParameters() {
+        return getTypeFromCache().getParameters();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Map<String, HApiProperty> getProperties() {
+        return getTypeFromCache().getProperties();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Map<String, HApiProperty> getAllProperties() {
+        return getAllProperties(this);
+    }
+
+    private Map<String, HApiProperty> getAllProperties(HApiType rootType) {
+        HApiType parent = getParent(rootType);
+        Map<String, HApiProperty> allProps = new HashMap<String, HApiProperty>();
+        LOG.debug("parent: {}", parent);
+        if (null != parent) {
+            Map<String, HApiProperty> parentProps;
+            if (parent instanceof HApiTypeLazyWrapper) {
+                parentProps = ((HApiTypeLazyWrapper) parent).getAllProperties(rootType);
+            } else {
+                parentProps = parent.getAllProperties();
+            }
+            LOG.debug("parent props: {}", parentProps);
+            allProps.putAll(parentProps);
+        }
+        allProps.putAll(getProperties());
+        return allProps;
+    }
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public HApiType getParent() {
+        return getTypeFromCache().getParent();
+    }
+
+    private HApiType getParent(HApiType rootType) {
+        if (this.equals(rootType)) return null;
+        return getParent();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean isAbstract() {
+        return getTypeFromCache().isAbstract();
+    }
+
+    @Override
+    public String toString() {
+        return "[Weak reference] "  + this.getName() + "(" + this.getPath() + "): Properties: " + this.getProperties();
+    }
+}
diff --git a/src/main/java/org/apache/sling/hapi/impl/HApiUtilImpl.java b/src/main/java/org/apache/sling/hapi/impl/HApiUtilImpl.java
index a140713..f1ae6e9 100644
--- a/src/main/java/org/apache/sling/hapi/impl/HApiUtilImpl.java
+++ b/src/main/java/org/apache/sling/hapi/impl/HApiUtilImpl.java
@@ -34,44 +34,72 @@ import javax.jcr.query.Query;
 import javax.jcr.query.QueryManager;
 import javax.jcr.query.QueryResult;
 
-import org.apache.felix.scr.annotations.Activate;
-import org.apache.felix.scr.annotations.Component;
-import org.apache.felix.scr.annotations.Property;
-import org.apache.felix.scr.annotations.Service;
 import org.apache.sling.api.resource.Resource;
 import org.apache.sling.api.resource.ResourceResolver;
 import org.apache.sling.api.resource.ValueMap;
-import org.apache.sling.commons.osgi.PropertiesUtil;
 import org.apache.sling.hapi.*;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.metatype.annotations.AttributeDefinition;
+import org.osgi.service.metatype.annotations.AttributeType;
+import org.osgi.service.metatype.annotations.Designate;
+import org.osgi.service.metatype.annotations.ObjectClassDefinition;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 
-@Component(label = "Apache Sling Hypermedia API tools", metatype = true)
-@Service(value = HApiUtil.class)
+@Component(
+        name = "Apache Sling Hypermedia API tools",
+        service = HApiUtil.class,
+        configurationPid = "org.apache.sling.hapi.impl.HApiUtilImpl"
+)
+@Designate(
+        ocd = HApiUtilImpl.Configuration.class
+)
 public class HApiUtilImpl implements HApiUtil {
 
     private final Logger LOG = LoggerFactory.getLogger(HApiUtil.class);
 
-    @Property(label = "HApi Resource Type", cardinality = 0, value = DEFAULT_RESOURCE_TYPE,
-            description = RESOURCE_TYPE_DESC)
-    public static final String HAPI_RESOURCE_TYPE = RESOURCE_TYPE;
+    @ObjectClassDefinition(
+            name = "Apache Sling Hypermedia API tools"
+    )
+    @interface Configuration {
 
-    @Property(label = "HApi Collection Resource Type", cardinality = 0, value = DEFAULT_COLLECTION_RESOURCE_TYPE,
-            description = COLLECTION_RESOURCE_TYPE_DESC)
-    private static final String HAPI_COLLECTION_RESOURCE_TYPE = COLLECTION_RESOURCE_TYPE;
+        @AttributeDefinition(
+                name = "HApi Resource Type",
+                description = RESOURCE_TYPE_DESC
 
-    @Property(label = "HApi Types Search Paths", cardinality=50, value = {DEFAULT_SEARCH_PATH},
-            description = SEARCH_PATHS_DESC)
-    public static final String HAPI_PATHS = SEARCH_PATHS;
+        )
+        String org_apache_sling_hapi_tools_resourcetype() default DEFAULT_RESOURCE_TYPE;
 
-    @Property(label = "External server URL", cardinality = 0, value = DEFAULT_SERVER_URL,
-            description = EXTERNAL_URL_DESC)
-    public static final String HAPI_EXTERNAL_URL = EXTERNAL_URL;
+        @AttributeDefinition(
+                name = "HApi Collection Resource Type",
+                description = COLLECTION_RESOURCE_TYPE_DESC
 
-    @Property(label = "Enabled", boolValue = DEFAULT_ENABLED,
-            description = ENABLED_DESC)
-    public static final String HAPI_ENABLED = ENABLED;
+        )
+        String org_apache_sling_hapi_tools_collectionresourcetype() default DEFAULT_COLLECTION_RESOURCE_TYPE;
+
+        @AttributeDefinition(
+                name = "HApi Types Search Paths",
+                description = SEARCH_PATHS_DESC
+        )
+        String[] org_apache_sling_hapi_tools_searchpaths() default {DEFAULT_SEARCH_PATH};
+
+        @AttributeDefinition(
+                name = "External server URL",
+                description = EXTERNAL_URL_DESC
+
+        )
+        String org_apache_sling_hapi_tools_externalurl() default DEFAULT_SERVER_URL;
+
+        @AttributeDefinition(
+                name = "Enabled",
+                description = ENABLED_DESC,
+                type = AttributeType.BOOLEAN
+        )
+        boolean org_apache_sling_hapi_tools_enabled() default DEFAULT_ENABLED;
+
+    }
 
 
     private static String resourceType;
@@ -82,15 +110,14 @@ public class HApiUtilImpl implements HApiUtil {
 
 
     @Activate
-    private void activate(Map<String, Object> configuration) {
-        enabled = PropertiesUtil.toBoolean(configuration.get(HAPI_ENABLED), false);
+    private void activate(HApiUtilImpl.Configuration configuration) {
+        enabled = configuration.org_apache_sling_hapi_tools_enabled();
         if (!enabled) return;
 
-        resourceType = PropertiesUtil.toString(configuration.get(HAPI_RESOURCE_TYPE), DEFAULT_RESOURCE_TYPE);
-        collectionResourceType = PropertiesUtil.toString(configuration.get(HAPI_COLLECTION_RESOURCE_TYPE),
-                DEFAULT_COLLECTION_RESOURCE_TYPE);
-        hApiPaths = PropertiesUtil.toStringArray(configuration.get(HAPI_PATHS));
-        serverContextPath = PropertiesUtil.toString(configuration.get(HAPI_EXTERNAL_URL), DEFAULT_SERVER_URL);
+        resourceType = configuration.org_apache_sling_hapi_tools_resourcetype();
+        collectionResourceType = configuration.org_apache_sling_hapi_tools_collectionresourcetype();
+        hApiPaths = configuration.org_apache_sling_hapi_tools_searchpaths();
+        serverContextPath = configuration.org_apache_sling_hapi_tools_externalurl();
     }
 
     /**
@@ -221,51 +248,36 @@ public class HApiUtilImpl implements HApiUtil {
         for (Value p : Arrays.asList(parameterValues)) {
             parameters.add(p.getString());
         }
-        HApiTypeImpl newType = new HApiTypeImpl(name, description, serverContextPath, path, fqdn, parameters, null, null, false);
-        TypesCache.getInstance(this).addType(newType);
-        LOG.debug("Inserted type {} to cache: {}", newType, TypesCache.getInstance(this));
 
-
-        try {
-            // Get parent if it exists
-            HApiType parent = null;
-            String parentPath = resProps.get("extends", (String) null);
-            if (null != parentPath) {
-                parent = TypesCache.getInstance(this).getType(resolver, getTypeResource(resolver, parentPath));
-            }
-
-            // Get properties
-            Map<String, HApiProperty> properties = new HashMap<String, HApiProperty>();
-            for (Resource res : typeResource.getChildren()) {
-                ValueMap resValueMap = res.adaptTo(ValueMap.class);
-
-                String propName = res.getName();
-                String propDescription = resValueMap.get("description", "");
-                String typeFqdnOrPath = resValueMap.get("type", (String) null);
-                Resource propTypeResource = getTypeResource(resolver, typeFqdnOrPath);
-                HApiType propType = (null != propTypeResource)
-                        ? TypesCache.getInstance(this).getType(resolver, propTypeResource)
-                        : new AbstractHapiTypeImpl(typeFqdnOrPath);
-                LOG.debug("Fetched type {} from cache", propType);
-                Boolean propMultiple = resValueMap.get("multiple", false);
-
-                HApiProperty prop = new HApiPropertyImpl(propName, propDescription, propType, propMultiple);
-                properties.put(prop.getName(), prop);
-            }
-            // Set parent and properties
-            newType.setParent(parent);
-            newType.setProperties(properties);
-
-        } catch (RuntimeException t) {
-            // Remove type from cache if it wasn't created successfully
-            TypesCache.getInstance(this).removeType(newType.getPath());
-            throw t;
-        } catch (RepositoryException e) {
-            // Remove type from cache if it wasn't created successfully
-            TypesCache.getInstance(this).removeType(newType.getPath());
-            throw e;
+        // Parent weak reference
+        String parentPath = resProps.get("extends", (String) null);
+        HApiType parentWeak = new HApiTypeLazyWrapper(this, resolver, this.serverContextPath, parentPath);
+
+        // Properties weak reference
+        Map<String, HApiProperty> properties = new HashMap<String, HApiProperty>();
+        for (Resource res : typeResource.getChildren()) {
+            ValueMap resValueMap = res.adaptTo(ValueMap.class);
+
+            String propName = res.getName();
+            String propDescription = resValueMap.get("description", "");
+            String typeFqdnOrPath = resValueMap.get("type", (String) null);
+            Resource propTypeResource = getTypeResource(resolver, typeFqdnOrPath);
+            HApiType propTypeWeak = (null != propTypeResource)
+                    ? new HApiTypeLazyWrapper(this, resolver, this.serverContextPath, propTypeResource)
+                    : new AbstractHapiTypeImpl(typeFqdnOrPath);
+            Boolean propMultiple = resValueMap.get("multiple", false);
+
+            HApiProperty prop = new HApiPropertyImpl(propName, propDescription, propTypeWeak, propMultiple);
+            properties.put(prop.getName(), prop);
         }
 
+        //
+        // Create type and add to cache
+        //
+        HApiTypeImpl newType = new HApiTypeImpl(name, description, serverContextPath, path, fqdn, parameters, properties, parentWeak, false);
+        TypesCache.getInstance(this).addType(newType);
+        LOG.debug("Inserted type {} to cache: {}", newType, TypesCache.getInstance(this));
+
         LOG.debug("Created type {}", newType);
         return newType;
     }

-- 
To stop receiving notification emails like this one, please contact
"commits@sling.apache.org" <co...@sling.apache.org>.