diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/osgi/OsgiUtil.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/osgi/OsgiUtil.java new file mode 100644 index 0000000..4d60b85 --- /dev/null +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/osgi/OsgiUtil.java @@ -0,0 +1,156 @@ +/* + * 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.jackrabbit.oak.osgi; + +import org.osgi.framework.BundleContext; +import org.osgi.service.component.ComponentContext; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Utility methods to use in an OSGi environment. + */ +public class OsgiUtil { + + private OsgiUtil() { + // Prevent instantiation. + } + + /** + * Looks a property up by name in a component context. Returns {@code null} + * if the property is not found or if the property is found but it is an + * empty string. + * + * @param context Component context. + * @param name Name of the property. + * @return The property value serialized as a string, or {@code null}. + */ + public static String lookup(ComponentContext context, String name) { + return asString(checkNotNull(context).getProperties().get(checkNotNull(name))); + } + + /** + * Looks a property up by name in the set of framework properties. Returns + * {@code null} if the property is not found or if the property is found but + * it is an empty string. + * + * @param context Bundle context. + * @param name Name of the property. + * @return The property value serialized as a string, or {@code null}. + */ + public static String lookup(BundleContext context, String name) { + return asString(checkNotNull(context).getProperty(checkNotNull(name))); + } + + /** + * Looks a property up by name in the component context first, falling back + * in the framework properties if not found. Returns {@code null} if the + * property is not found or if the property is found but it is an empty + * string. + * + * @param context Component context. + * @param name Name of the property. + * @return The property value serialized as a string, or {@code null}. + */ + public static String lookupConfigurationThenFramework(ComponentContext context, String name) { + return lookupConfigurationThenFramework(context, name, name); + } + + /** + * Looks a property up by name in the component context first, falling back + * in the framework properties if not found. Returns {@code null} if the + * property is not found or if the property is found but it is an empty + * string. + * + * @param context Component context. + * @param nameInComponent Name of the property in the component context. + * @param nameInFramework Name of the property in the framework properties. + * @return The property value serialized as a string, or {@code null}. + */ + public static String lookupConfigurationThenFramework(ComponentContext context, String nameInComponent, String nameInFramework) { + String fromComponent = lookup(context, nameInComponent); + + if (fromComponent != null) { + return fromComponent; + } + + String fromFramework = lookup(context.getBundleContext(), nameInFramework); + + if (fromFramework != null) { + return fromFramework; + } + + return null; + } + + /** + * Looks a property up by name in the framework properties first, falling + * back to the component context if not not found. Returns {@code null} if + * the property is not found or if the property is found but it is an empty + * string. + * + * @param context Component context. + * @param name Name of the property. + * @return The property value serialized as a string, or {@code null}. + */ + public static String lookupFrameworkThenConfiguration(ComponentContext context, String name) { + return lookupFrameworkThenConfiguration(context, name, name); + } + + /** + * Looks a property up by name in the framework properties first, falling + * back to the component context if not not found. Returns {@code null} if + * the property is not found or if the property is found but it is an empty + * string. + * + * @param context Component context. + * @param nameInComponent Name of the property in the component context. + * @param nameInFramework Name of the property in the framework properties. + * @return The property value serialized as a string, or {@code null}. + */ + public static String lookupFrameworkThenConfiguration(ComponentContext context, String nameInComponent, String nameInFramework) { + String fromFramework = lookup(checkNotNull(context).getBundleContext(), nameInFramework); + + if (fromFramework != null) { + return fromFramework; + } + + String fromComponent = lookup(context, nameInComponent); + + if (fromComponent != null) { + return fromComponent; + } + + return null; + } + + private static String asString(Object value) { + if (value == null) { + return null; + } + + String string = value.toString().trim(); + + if (string.isEmpty()) { + return null; + } + + return string; + } + +} diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/osgi/package-info.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/osgi/package-info.java index aafedef..89473ab 100644 --- a/oak-core/src/main/java/org/apache/jackrabbit/oak/osgi/package-info.java +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/osgi/package-info.java @@ -14,9 +14,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -@Version("1.0") +@Version("2.0") @Export(optional = "provide:=true") package org.apache.jackrabbit.oak.osgi; import aQute.bnd.annotation.Version; -import aQute.bnd.annotation.Export; \ No newline at end of file +import aQute.bnd.annotation.Export; diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentNodeStoreService.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentNodeStoreService.java index bef14a3..ae9883c 100644 --- a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentNodeStoreService.java +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentNodeStoreService.java @@ -21,6 +21,7 @@ import static java.util.Collections.emptyMap; import static org.apache.jackrabbit.oak.commons.PropertiesUtil.toBoolean; import static org.apache.jackrabbit.oak.commons.PropertiesUtil.toInteger; import static org.apache.jackrabbit.oak.commons.PropertiesUtil.toLong; +import static org.apache.jackrabbit.oak.osgi.OsgiUtil.lookupConfigurationThenFramework; import static org.apache.jackrabbit.oak.plugins.segment.compaction.CompactionStrategy.CLEANUP_DEFAULT; import static org.apache.jackrabbit.oak.plugins.segment.compaction.CompactionStrategy.CLONE_BINARIES_DEFAULT; import static org.apache.jackrabbit.oak.plugins.segment.compaction.CompactionStrategy.FORCE_AFTER_FAIL_DEFAULT; @@ -271,7 +272,7 @@ public class SegmentNodeStoreService extends ProxyNodeStore @Activate private void activate(ComponentContext context) throws IOException { this.context = context; - this.customBlobStore = Boolean.parseBoolean(lookup(context, CUSTOM_BLOB_STORE)); + this.customBlobStore = Boolean.parseBoolean(property(CUSTOM_BLOB_STORE)); if (blobStore == null && customBlobStore) { log.info("BlobStore use enabled. SegmentNodeStore would be initialized when BlobStore would be available"); @@ -282,7 +283,7 @@ public class SegmentNodeStoreService extends ProxyNodeStore public void registerNodeStore() throws IOException { if (registerSegmentStore()) { - boolean standby = toBoolean(lookup(context, STANDBY), false); + boolean standby = toBoolean(property(STANDBY), false); providerRegistration = context.getBundleContext().registerService( SegmentStoreProvider.class.getName(), this, null); if (!standby) { @@ -303,56 +304,56 @@ public class SegmentNodeStoreService extends ProxyNodeStore Dictionary properties = context.getProperties(); name = String.valueOf(properties.get(NAME)); - String directory = lookup(context, DIRECTORY); + String directory = property(DIRECTORY); if (directory == null) { directory = "tarmk"; }else{ directory = FilenameUtils.concat(directory, "segmentstore"); } - String mode = lookup(context, MODE); + String mode = property(MODE); if (mode == null) { mode = System.getProperty(MODE, System.getProperty("sun.arch.data.model", "32")); } - String size = lookup(context, SIZE); + String size = property(SIZE); if (size == null) { size = System.getProperty(SIZE, "256"); } - String cache = lookup(context, CACHE); + String cache = property(CACHE); if (cache == null) { cache = System.getProperty(CACHE); } - boolean pauseCompaction = toBoolean(lookup(context, PAUSE_COMPACTION), + boolean pauseCompaction = toBoolean(property(PAUSE_COMPACTION), PAUSE_DEFAULT); boolean cloneBinaries = toBoolean( - lookup(context, COMPACTION_CLONE_BINARIES), + property(COMPACTION_CLONE_BINARIES), CLONE_BINARIES_DEFAULT); - long cleanupTs = toLong(lookup(context, COMPACTION_CLEANUP_TIMESTAMP), + long cleanupTs = toLong(property(COMPACTION_CLEANUP_TIMESTAMP), TIMESTAMP_DEFAULT); - int retryCount = toInteger(lookup(context, COMPACTION_RETRY_COUNT), + int retryCount = toInteger(property(COMPACTION_RETRY_COUNT), RETRY_COUNT_DEFAULT); - boolean forceCommit = toBoolean(lookup(context, COMPACTION_FORCE_AFTER_FAIL), + boolean forceCommit = toBoolean(property(COMPACTION_FORCE_AFTER_FAIL), FORCE_AFTER_FAIL_DEFAULT); - final int lockWaitTime = toInteger(lookup(context, COMPACTION_LOCK_WAIT_TIME), + final int lockWaitTime = toInteger(property(COMPACTION_LOCK_WAIT_TIME), COMPACTION_LOCK_WAIT_TIME_DEFAULT); - boolean persistCompactionMap = toBoolean(lookup(context, PERSIST_COMPACTION_MAP), + boolean persistCompactionMap = toBoolean(property(PERSIST_COMPACTION_MAP), PERSIST_COMPACTION_MAP_DEFAULT); - String cleanup = lookup(context, COMPACTION_CLEANUP); + String cleanup = property(COMPACTION_CLEANUP); if (cleanup == null) { cleanup = CLEANUP_DEFAULT.toString(); } - String memoryThresholdS = lookup(context, COMPACTION_MEMORY_THRESHOLD); + String memoryThresholdS = property(COMPACTION_MEMORY_THRESHOLD); byte memoryThreshold = MEMORY_THRESHOLD_DEFAULT; if (memoryThresholdS != null) { memoryThreshold = Byte.valueOf(memoryThresholdS); } - final long blobGcMaxAgeInSecs = toLong(lookup(context, PROP_BLOB_GC_MAX_AGE), DEFAULT_BLOB_GC_MAX_AGE); + final long blobGcMaxAgeInSecs = toLong(property(PROP_BLOB_GC_MAX_AGE), DEFAULT_BLOB_GC_MAX_AGE); OsgiWhiteboard whiteboard = new OsgiWhiteboard(context.getBundleContext()); gcMonitor = new GCMonitorTracker(); @@ -442,14 +443,8 @@ public class SegmentNodeStoreService extends ProxyNodeStore return true; } - private static String lookup(ComponentContext context, String property) { - if (context.getProperties().get(property) != null) { - return context.getProperties().get(property).toString(); - } - if (context.getBundleContext().getProperty(property) != null) { - return context.getBundleContext().getProperty(property); - } - return null; + private String property(String name) { + return lookupConfigurationThenFramework(context, name); } @Deactivate diff --git a/oak-core/src/test/java/org/apache/jackrabbit/oak/osgi/OsgiUtilTest.java b/oak-core/src/test/java/org/apache/jackrabbit/oak/osgi/OsgiUtilTest.java new file mode 100644 index 0000000..7546cc2 --- /dev/null +++ b/oak-core/src/test/java/org/apache/jackrabbit/oak/osgi/OsgiUtilTest.java @@ -0,0 +1,207 @@ +/* + * 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.jackrabbit.oak.osgi; + +import org.junit.Test; +import org.osgi.framework.BundleContext; +import org.osgi.service.component.ComponentContext; + +import java.util.Dictionary; + +import static org.apache.jackrabbit.oak.osgi.OsgiUtil.lookup; +import static org.apache.jackrabbit.oak.osgi.OsgiUtil.lookupConfigurationThenFramework; +import static org.apache.jackrabbit.oak.osgi.OsgiUtil.lookupFrameworkThenConfiguration; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; + +public class OsgiUtilTest { + + @Test(expected = NullPointerException.class) + public void testComponentLookupWithNullContext() { + lookup((ComponentContext) null, "name"); + } + + @Test(expected = NullPointerException.class) + public void testComponentLookupWithNullName() { + lookup(mock(ComponentContext.class), null); + } + + @Test + public void testComponentLookupWithNotFoundValue() { + Dictionary properties = mock(Dictionary.class); + doReturn(null).when(properties).get("name"); + + ComponentContext context = mock(ComponentContext.class); + doReturn(properties).when(context).getProperties(); + + assertNull(lookup(context, "name")); + } + + @Test + public void testComponentLookupWithEmptyString() { + Dictionary properties = mock(Dictionary.class); + doReturn("").when(properties).get("name"); + + ComponentContext context = mock(ComponentContext.class); + doReturn(properties).when(context).getProperties(); + + assertNull(lookup(context, "name")); + } + + @Test + public void testComponentLookupWithNonTrimmedString() { + Dictionary properties = mock(Dictionary.class); + doReturn(" ").when(properties).get("name"); + + ComponentContext context = mock(ComponentContext.class); + doReturn(properties).when(context).getProperties(); + + assertNull(lookup(context, "name")); + } + + @Test + public void testComponentLookupWithNonStringValue() { + Dictionary properties = mock(Dictionary.class); + doReturn(42).when(properties).get("name"); + + ComponentContext context = mock(ComponentContext.class); + doReturn(properties).when(context).getProperties(); + + assertEquals("42", lookup(context, "name")); + } + + @Test + public void testComponentLookupWithStringValue() { + Dictionary properties = mock(Dictionary.class); + doReturn(" value ").when(properties).get("name"); + + ComponentContext context = mock(ComponentContext.class); + doReturn(properties).when(context).getProperties(); + + assertEquals("value", lookup(context, "name")); + } + + @Test(expected = NullPointerException.class) + public void testFrameworkLookupWithNullContext() { + lookup((BundleContext) null, "name"); + } + + @Test(expected = NullPointerException.class) + public void testFrameworkLookupWithNullName() { + lookup(mock(BundleContext.class), null); + } + + @Test + public void testFrameworkLookupWithNotFoundValue() { + BundleContext context = mock(BundleContext.class); + doReturn(null).when(context).getProperty("name"); + + assertNull(lookup(context, "name")); + } + + @Test + public void testFrameworkLookupWithEmptyString() { + BundleContext context = mock(BundleContext.class); + doReturn("").when(context).getProperty("name"); + + assertNull(lookup(context, "name")); + } + + @Test + public void testFrameworkLookupWithNonTrimmedString() { + BundleContext context = mock(BundleContext.class); + doReturn(" ").when(context).getProperty("name"); + + assertNull(lookup(context, "name")); + } + + @Test + public void testFrameworkLookupWith() { + BundleContext context = mock(BundleContext.class); + doReturn(" value ").when(context).getProperty("name"); + + assertEquals("value", lookup(context, "name")); + } + + @Test + public void testFallbackLookupWithNoValue() { + Dictionary dictionary = mock(Dictionary.class); + doReturn(null).when(dictionary).get("cname"); + + BundleContext bundleContext = mock(BundleContext.class); + doReturn(null).when(bundleContext).getProperty("fname"); + + ComponentContext componentContext = mock(ComponentContext.class); + doReturn(dictionary).when(componentContext).getProperties(); + doReturn(bundleContext).when(componentContext).getBundleContext(); + + assertNull(lookupConfigurationThenFramework(componentContext, "cname", "fname")); + assertNull(lookupFrameworkThenConfiguration(componentContext, "cname", "fname")); + } + + @Test + public void testFallbackLookupWithValueInComponent() { + Dictionary dictionary = mock(Dictionary.class); + doReturn("value").when(dictionary).get("cname"); + + BundleContext bundleContext = mock(BundleContext.class); + doReturn(null).when(bundleContext).getProperty("fname"); + + ComponentContext componentContext = mock(ComponentContext.class); + doReturn(dictionary).when(componentContext).getProperties(); + doReturn(bundleContext).when(componentContext).getBundleContext(); + + assertEquals("value", lookupConfigurationThenFramework(componentContext, "cname", "fname")); + assertEquals("value", lookupFrameworkThenConfiguration(componentContext, "cname", "fname")); + } + + @Test + public void testFallbackLookupWithValueInFramework() { + Dictionary dictionary = mock(Dictionary.class); + doReturn(null).when(dictionary).get("cname"); + + BundleContext bundleContext = mock(BundleContext.class); + doReturn("value").when(bundleContext).getProperty("fname"); + + ComponentContext componentContext = mock(ComponentContext.class); + doReturn(dictionary).when(componentContext).getProperties(); + doReturn(bundleContext).when(componentContext).getBundleContext(); + + assertEquals("value", lookupConfigurationThenFramework(componentContext, "cname", "fname")); + assertEquals("value", lookupFrameworkThenConfiguration(componentContext, "cname", "fname")); + } + + @Test + public void testFallbackLookupWithValueInComponentAndFramework() { + Dictionary dictionary = mock(Dictionary.class); + doReturn("cvalue").when(dictionary).get("cname"); + + BundleContext bundleContext = mock(BundleContext.class); + doReturn("fvalue").when(bundleContext).getProperty("fname"); + + ComponentContext componentContext = mock(ComponentContext.class); + doReturn(dictionary).when(componentContext).getProperties(); + doReturn(bundleContext).when(componentContext).getBundleContext(); + + assertEquals("cvalue", lookupConfigurationThenFramework(componentContext, "cname", "fname")); + assertEquals("fvalue", lookupFrameworkThenConfiguration(componentContext, "cname", "fname")); + } + +}