Author: tomekr
Date: Wed Sep 21 12:35:02 2016
New Revision: 1761723

URL: http://svn.apache.org/viewvc?rev=1761723&view=rev
Log:
OAK-4655: Enable configuring multiple segment nodestore instances in same setup

Added:
    
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreFactory.java
    
jackrabbit/oak/trunk/oak-segment/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentNodeStoreFactory.java

Added: 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreFactory.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreFactory.java?rev=1761723&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreFactory.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreFactory.java
 Wed Sep 21 12:35:02 2016
@@ -0,0 +1,308 @@
+/*
+ * 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.segment;
+
+import static com.google.common.base.Preconditions.checkState;
+import static 
org.apache.jackrabbit.oak.osgi.OsgiUtil.lookupConfigurationThenFramework;
+import static 
org.apache.jackrabbit.oak.segment.file.FileStoreBuilder.fileStoreBuilder;
+import static 
org.apache.jackrabbit.oak.spi.whiteboard.WhiteboardUtils.registerMBean;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.ConfigurationPolicy;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.jackrabbit.oak.commons.PropertiesUtil;
+import org.apache.jackrabbit.oak.osgi.OsgiWhiteboard;
+import org.apache.jackrabbit.oak.segment.file.FileStore;
+import org.apache.jackrabbit.oak.segment.file.FileStoreBuilder;
+import org.apache.jackrabbit.oak.segment.file.FileStoreStatsMBean;
+import org.apache.jackrabbit.oak.segment.file.InvalidFileStoreVersionException;
+import org.apache.jackrabbit.oak.spi.state.NodeStore;
+import org.apache.jackrabbit.oak.spi.state.NodeStoreProvider;
+import org.apache.jackrabbit.oak.spi.state.ProxyNodeStore;
+import org.apache.jackrabbit.oak.spi.whiteboard.Registration;
+import org.apache.jackrabbit.oak.spi.whiteboard.WhiteboardExecutor;
+import org.apache.jackrabbit.oak.stats.StatisticsProvider;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.component.ComponentContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A factory allowing creation of secondary segment node stores.
+ * <p>
+ * The different secondaries are distinguished by their role attribute.
+ */
+@Component(policy = ConfigurationPolicy.REQUIRE,
+        name="org.apache.jackrabbit.oak.segment.SegmentNodeStoreFactory",
+        configurationFactory=true,
+        metatype = true,
+        label = "Apache Jackrabbit Oak Segment-Tar NodeStore Factory",
+        description = "Factory allowing configuration of adjacent instances of 
" +
+                      "NodeStore implementation based on Segment model besides 
a default SegmentNodeStore in same setup."
+)
+public class SegmentNodeStoreFactory extends ProxyNodeStore
+        implements SegmentStoreProvider {
+
+    public static final String NAME = "name";
+
+    @Property(
+            label = "Role",
+            description="As multiple SegmentNodeStores can be configured, this 
parameter defines the role " +
+                        "of 'this' SegmentNodeStore."
+    )
+    public static final String ROLE = "nsProvider.role";
+
+    @Property(
+            label = "Directory",
+            description="Directory location used to store the segment tar 
files. If not specified then looks " +
+                        "for framework property 'repository.home' otherwise 
use a subdirectory with name 'tarmk'"
+    )
+    public static final String DIRECTORY = "repository.home";
+
+    @Property(
+            label = "Mode",
+            description="TarMK mode (64 for memory mapping, 32 for normal file 
access)"
+    )
+    public static final String MODE = "tarmk.mode";
+
+    @Property(
+            intValue = 256,
+            label = "Maximum Tar File Size (MB)",
+            description = "TarMK maximum file size (MB)"
+    )
+    public static final String SIZE = "tarmk.size";
+
+    @Property(
+            intValue = 256,
+            label = "Cache size (MB)",
+            description = "Cache size for storing most recently used Segments"
+    )
+    public static final String CACHE = "cache";
+
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    private String name;
+
+    private FileStore store;
+
+    private volatile SegmentNodeStore segmentNodeStore;
+
+    private ComponentContext context;
+
+    @Reference
+    private StatisticsProvider statisticsProvider = StatisticsProvider.NOOP;
+
+    private ServiceRegistration storeRegistration;
+    private Registration fileStoreStatsMBean;
+    private WhiteboardExecutor executor;
+
+    @Override
+    protected SegmentNodeStore getNodeStore() {
+        checkState(segmentNodeStore != null, "service must be activated when 
used");
+        return segmentNodeStore;
+    }
+    
+    private String getRole() {
+        String role = PropertiesUtil.toString(property(ROLE), null);
+        return role;
+    }
+    
+    @Activate
+    public void activate(ComponentContext context) throws IOException {
+        this.context = context;
+        log.info("activate: SegmentNodeStore '"+getRole()+"' starting.");
+
+        registerNodeStore();
+    }
+
+    @Deactivate
+    public void deactivate() {
+        unregisterNodeStore();
+
+        synchronized (this) {
+            segmentNodeStore = null;
+
+            if (store != null) {
+                store.close();
+                store = null;
+            }
+        }
+    }
+
+    private synchronized void registerNodeStore() throws IOException {
+        if (registerSegmentStore()) {
+
+            if (getRole() != null) {
+                registerNodeStoreProvider();
+                return;
+            }
+
+        }
+    }
+
+    private void registerNodeStoreProvider() {
+        SegmentNodeStore.SegmentNodeStoreBuilder nodeStoreBuilder = 
SegmentNodeStoreBuilders.builder(store);
+        segmentNodeStore = nodeStoreBuilder.build();
+        Dictionary<String, Object> props = new Hashtable<String, Object>();
+        props.put(NodeStoreProvider.ROLE, getRole());
+        storeRegistration = 
context.getBundleContext().registerService(NodeStoreProvider.class.getName(), 
new NodeStoreProvider() {
+                    @Override
+                    public NodeStore getNodeStore() {
+                        return SegmentNodeStoreFactory.this;
+                    }
+                },
+                props);
+        log.info("Registered NodeStoreProvider backed by SegmentNodeStore of 
type '{}'", getRole());
+    }
+
+    private boolean registerSegmentStore() throws IOException {
+        if (context == null) {
+            log.info("Component still not activated. Ignoring the 
initialization call");
+            return false;
+        }
+
+        OsgiWhiteboard whiteboard = new 
OsgiWhiteboard(context.getBundleContext());
+
+        // Build the FileStore
+
+        FileStoreBuilder builder = fileStoreBuilder(getDirectory())
+                .withSegmentCacheSize(getCacheSize())
+                .withMaxFileSize(getMaxFileSize())
+                .withMemoryMapping(getMode().equals("64"))
+                .withStatisticsProvider(statisticsProvider);
+
+        try {
+            store = builder.build();
+        } catch (InvalidFileStoreVersionException e) {
+            log.error("The segment store data is not compatible with the 
current version. Please use oak-segment-tar or a different version of 
oak-segment.");
+            return false;
+        }
+
+        // Listen for Executor services on the whiteboard
+
+        executor = new WhiteboardExecutor();
+        executor.start(whiteboard);
+
+        // Expose statistics about the FileStore
+
+        fileStoreStatsMBean = registerMBean(
+                whiteboard,
+                FileStoreStatsMBean.class,
+                store.getStats(),
+                FileStoreStatsMBean.TYPE,
+                "FileStore '" + getRole() + "' statistics"
+        );
+
+        return true;
+    }
+
+    private void unregisterNodeStore() {
+        if (storeRegistration != null) {
+            storeRegistration.unregister();
+            storeRegistration = null;
+        }
+        if (fileStoreStatsMBean != null) {
+            fileStoreStatsMBean.unregister();
+            fileStoreStatsMBean = null;
+        }
+        if (executor != null) {
+            executor.stop();
+            executor = null;
+        }
+    }
+
+    private File getBaseDirectory() {
+        String directory = property(DIRECTORY);
+
+        if (directory != null) {
+            return new File(directory);
+        }
+
+        return new File("tarmk");
+    }
+
+    private File getDirectory() {
+        return new File(getBaseDirectory(), "segmentstore");
+    }
+
+    private String getMode() {
+        String mode = property(MODE);
+
+        if (mode != null) {
+            return mode;
+        }
+
+        return System.getProperty(MODE, 
System.getProperty("sun.arch.data.model", "32"));
+    }
+
+    private String getCacheSizeProperty() {
+        String cache = property(CACHE);
+
+        if (cache != null) {
+            return cache;
+        }
+
+        return System.getProperty(CACHE);
+    }
+
+    private int getCacheSize() {
+        return Integer.parseInt(getCacheSizeProperty());
+    }
+
+    private String getMaxFileSizeProperty() {
+        String size = property(SIZE);
+
+        if (size != null) {
+            return size;
+        }
+
+        return System.getProperty(SIZE, "256");
+    }
+
+    private int getMaxFileSize() {
+        return Integer.parseInt(getMaxFileSizeProperty());
+    }
+
+    private String property(String name) {
+        return lookupConfigurationThenFramework(context, name);
+    }
+
+    /**
+     * needed for situations where you have to unwrap the
+     * SegmentNodeStoreService, to get the SegmentStore, like the failover
+     */
+    @Override
+    public SegmentStore getSegmentStore() {
+        return store;
+    }
+
+    //------------------------------------------------------------< Object >--
+
+    @Override
+    public String toString() {
+        return name + ": " + segmentNodeStore + "[role:" + getRole() + "]";
+    }
+
+}

Added: 
jackrabbit/oak/trunk/oak-segment/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentNodeStoreFactory.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentNodeStoreFactory.java?rev=1761723&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-segment/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentNodeStoreFactory.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-segment/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentNodeStoreFactory.java
 Wed Sep 21 12:35:02 2016
@@ -0,0 +1,307 @@
+/*
+ * 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.plugins.segment;
+
+import static com.google.common.base.Preconditions.checkState;
+import static 
org.apache.jackrabbit.oak.osgi.OsgiUtil.lookupConfigurationThenFramework;
+import static 
org.apache.jackrabbit.oak.spi.whiteboard.WhiteboardUtils.registerMBean;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.ConfigurationPolicy;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.jackrabbit.oak.commons.PropertiesUtil;
+import org.apache.jackrabbit.oak.osgi.OsgiWhiteboard;
+import org.apache.jackrabbit.oak.plugins.segment.file.FileStore;
+import org.apache.jackrabbit.oak.plugins.segment.file.FileStore.Builder;
+import org.apache.jackrabbit.oak.plugins.segment.file.FileStoreStatsMBean;
+import 
org.apache.jackrabbit.oak.plugins.segment.file.InvalidFileStoreVersionException;
+import org.apache.jackrabbit.oak.spi.state.NodeStore;
+import org.apache.jackrabbit.oak.spi.state.NodeStoreProvider;
+import org.apache.jackrabbit.oak.spi.state.ProxyNodeStore;
+import org.apache.jackrabbit.oak.spi.whiteboard.Registration;
+import org.apache.jackrabbit.oak.spi.whiteboard.WhiteboardExecutor;
+import org.apache.jackrabbit.oak.stats.StatisticsProvider;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.component.ComponentContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A factory allowing creation of secondary segment node stores.
+ * <p>
+ * The different secondaries are distinguished by their role attribute.
+ */
+@Component(policy = ConfigurationPolicy.REQUIRE,
+        
name="org.apache.jackrabbit.oak.plugins.segment.SegmentNodeStoreFactory",
+        configurationFactory=true,
+        metatype = true,
+        label = "Apache Jackrabbit Oak Segment NodeStore Factory",
+        description = "Factory allowing configuration of adjacent instances of 
" +
+                      "NodeStore implementation based on Segment model besides 
a default SegmentNodeStore in same setup."
+)
+public class SegmentNodeStoreFactory extends ProxyNodeStore
+        implements SegmentStoreProvider {
+
+    public static final String NAME = "name";
+
+    @Property(
+            label = "Role",
+            description="As multiple SegmentNodeStores can be configured, this 
parameter defines the role " +
+                        "of 'this' SegmentNodeStore."
+    )
+    public static final String ROLE = "nsProvider.role";
+
+    @Property(
+            label = "Directory",
+            description="Directory location used to store the segment tar 
files. If not specified then looks " +
+                    "for framework property 'repository.home' otherwise use a 
subdirectory with name 'tarmk'"
+    )
+    public static final String DIRECTORY = "repository.home";
+
+    @Property(
+            label = "Mode",
+            description="TarMK mode (64 for memory mapping, 32 for normal file 
access)"
+    )
+    public static final String MODE = "tarmk.mode";
+
+    @Property(
+            intValue = 256,
+            label = "Maximum Tar File Size (MB)",
+            description = "TarMK maximum file size (MB)"
+    )
+    public static final String SIZE = "tarmk.size";
+
+    @Property(
+            intValue = 256,
+            label = "Cache size (MB)",
+            description = "Cache size for storing most recently used Segments"
+    )
+    public static final String CACHE = "cache";
+    
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    private String name;
+
+    private FileStore store;
+
+    private volatile SegmentNodeStore segmentNodeStore;
+
+    private ComponentContext context;
+
+    @Reference
+    private StatisticsProvider statisticsProvider = StatisticsProvider.NOOP;
+
+    private ServiceRegistration storeRegistration;
+    private Registration fileStoreStatsMBean;
+    private WhiteboardExecutor executor;
+
+    @Override
+    protected SegmentNodeStore getNodeStore() {
+        checkState(segmentNodeStore != null, "service must be activated when 
used");
+        return segmentNodeStore;
+    }
+    
+    private String getRole() {
+        String role = PropertiesUtil.toString(property(ROLE), null);
+        return role;
+    }
+    
+    @Activate
+    public void activate(ComponentContext context) throws IOException {
+        this.context = context;
+        log.info("activate: SegmentNodeStore '"+getRole()+"' starting.");
+
+        registerNodeStore();
+    }
+
+    @Deactivate
+    public void deactivate() {
+        unregisterNodeStore();
+
+        synchronized (this) {
+            segmentNodeStore = null;
+
+            if (store != null) {
+                store.close();
+                store = null;
+            }
+        }
+    }
+
+    private synchronized void registerNodeStore() throws IOException {
+        if (registerSegmentStore()) {
+
+            if (getRole() != null) {
+                registerNodeStoreProvider();
+                return;
+            }
+
+        }
+    }
+
+    private void registerNodeStoreProvider() {
+        SegmentNodeStore.SegmentNodeStoreBuilder nodeStoreBuilder = 
SegmentNodeStore.builder(store);
+        segmentNodeStore = nodeStoreBuilder.build();
+        Dictionary<String, Object> props = new Hashtable<String, Object>();
+        props.put(NodeStoreProvider.ROLE, getRole());
+        storeRegistration = 
context.getBundleContext().registerService(NodeStoreProvider.class.getName(), 
new NodeStoreProvider() {
+                    @Override
+                    public NodeStore getNodeStore() {
+                        return SegmentNodeStoreFactory.this;
+                    }
+                },
+                props);
+        log.info("Registered NodeStoreProvider backed by SegmentNodeStore of 
type '{}'", getRole());
+    }
+
+    private boolean registerSegmentStore() throws IOException {
+        if (context == null) {
+            log.info("Component still not activated. Ignoring the 
initialization call");
+            return false;
+        }
+
+        OsgiWhiteboard whiteboard = new 
OsgiWhiteboard(context.getBundleContext());
+
+        // Build the FileStore
+
+        Builder builder = FileStore.builder(getDirectory())
+                .withCacheSize(getCacheSize())
+                .withMaxFileSize(getMaxFileSize())
+                .withMemoryMapping(getMode().equals("64"))
+                .withStatisticsProvider(statisticsProvider);
+
+        try {
+            store = builder.build();
+        } catch (InvalidFileStoreVersionException e) {
+            log.error("The segment store data is not compatible with the 
current version. Please use oak-segment-tar or a different version of 
oak-segment.");
+            return false;
+        }
+
+        // Listen for Executor services on the whiteboard
+
+        executor = new WhiteboardExecutor();
+        executor.start(whiteboard);
+
+        // Expose statistics about the FileStore
+
+        fileStoreStatsMBean = registerMBean(
+                whiteboard,
+                FileStoreStatsMBean.class,
+                store.getStats(),
+                FileStoreStatsMBean.TYPE,
+                "FileStore '" + getRole() + "' statistics"
+        );
+
+        return true;
+    }
+
+    private void unregisterNodeStore() {
+        if (storeRegistration != null) {
+            storeRegistration.unregister();
+            storeRegistration = null;
+        }
+        if (fileStoreStatsMBean != null) {
+            fileStoreStatsMBean.unregister();
+            fileStoreStatsMBean = null;
+        }
+        if (executor != null) {
+            executor.stop();
+            executor = null;
+        }
+    }
+
+    private File getBaseDirectory() {
+        String directory = property(DIRECTORY);
+
+        if (directory != null) {
+            return new File(directory);
+        }
+
+        return new File("tarmk");
+    }
+
+    private File getDirectory() {
+        return new File(getBaseDirectory(), "segmentstore");
+    }
+
+    private String getMode() {
+        String mode = property(MODE);
+
+        if (mode != null) {
+            return mode;
+        }
+
+        return System.getProperty(MODE, 
System.getProperty("sun.arch.data.model", "32"));
+    }
+
+    private String getCacheSizeProperty() {
+        String cache = property(CACHE);
+
+        if (cache != null) {
+            return cache;
+        }
+
+        return System.getProperty(CACHE);
+    }
+
+    private int getCacheSize() {
+        return Integer.parseInt(getCacheSizeProperty());
+    }
+
+    private String getMaxFileSizeProperty() {
+        String size = property(SIZE);
+
+        if (size != null) {
+            return size;
+        }
+
+        return System.getProperty(SIZE, "256");
+    }
+
+    private int getMaxFileSize() {
+        return Integer.parseInt(getMaxFileSizeProperty());
+    }
+
+    private String property(String name) {
+        return lookupConfigurationThenFramework(context, name);
+    }
+
+    /**
+     * needed for situations where you have to unwrap the
+     * SegmentNodeStoreService, to get the SegmentStore, like the failover
+     */
+    @Override
+    public SegmentStore getSegmentStore() {
+        return store;
+    }
+
+    //------------------------------------------------------------< Object >--
+
+    @Override
+    public String toString() {
+        return name + ": " + segmentNodeStore + "[role:" + getRole() + "]";
+    }
+
+}


Reply via email to