diff --git a/sentinel-demo/sentinel-demo-dynamic-file-rule/src/main/java/com/alibaba/csp/sentinel/demo/file/rule/JarFileDataSourceDemo.java b/sentinel-demo/sentinel-demo-dynamic-file-rule/src/main/java/com/alibaba/csp/sentinel/demo/file/rule/JarFileDataSourceDemo.java
new file mode 100644
index 00000000..a36f21da
--- /dev/null
+++ b/sentinel-demo/sentinel-demo-dynamic-file-rule/src/main/java/com/alibaba/csp/sentinel/demo/file/rule/JarFileDataSourceDemo.java
@@ -0,0 +1,70 @@
+package com.alibaba.csp.sentinel.demo.file.rule;
+
+import com.alibaba.csp.sentinel.datasource.Converter;
+import com.alibaba.csp.sentinel.datasource.FileInJarReadableDataSource;
+import com.alibaba.csp.sentinel.datasource.ReadableDataSource;
+import com.alibaba.csp.sentinel.property.PropertyListener;
+import com.alibaba.csp.sentinel.property.SentinelProperty;
+import com.alibaba.csp.sentinel.slots.block.Rule;
+import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
+import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
+import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
+import com.alibaba.csp.sentinel.slots.system.SystemRuleManager;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.TypeReference;
+
+import java.util.List;
+
+/**
+ *
+ * This Demo shows how to use {@link FileInJarReadableDataSource} to read {@link Rule}s from jarfile. The
+ * {@link FileInJarReadableDataSource} will automatically fetches the backend file every 3 seconds, and
+ * inform the listener if the file is updated.
+ *
+ *
+ * Each {@link ReadableDataSource} has a {@link SentinelProperty} to hold the deserialized config data.
+ * {@link PropertyListener} will listen to the {@link SentinelProperty} instead of the datasource.
+ * {@link Converter} is used for telling how to deserialize the data.
+ *
+ *
+ * {@link FlowRuleManager#register2Property(SentinelProperty)},
+ * {@link DegradeRuleManager#register2Property(SentinelProperty)},
+ * {@link SystemRuleManager#register2Property(SentinelProperty)} could be called for listening the
+ * {@link Rule}s change.
+ *
+ *
+ * For other kinds of data source, such as Nacos,
+ * Zookeeper, Git, or even CSV file, We could implement {@link ReadableDataSource} interface to read these
+ * configs.
+ *
+ *
+ * @author dingq
+ * @date 2019-03-30
+ */
+public class JarFileDataSourceDemo {
+ public static void main(String[] args) throws Exception {
+ JarFileDataSourceDemo demo = new JarFileDataSourceDemo();
+ demo.listenRules();
+
+ /*
+ * Start to require tokens, rate will be limited by rule in FlowRule.json
+ */
+ FlowQpsRunner runner = new FlowQpsRunner();
+ runner.simulateTraffic();
+ runner.tick();
+ }
+
+ private void listenRules() throws Exception {
+ String jarPath = System.getProperty("user.dir") + "/sentinel-demo/sentinel-demo-dynamic-file-rule/target/sentinel-demo-dynamic-file-rule-1.5.1-SNAPSHOT.jar";
+ // eg: if flowRuleInJarName full path is 'sentinel-demo-dynamic-file-rule-1.5.1-SNAPSHOT.jar!/classes/FlowRule.json',
+ // your flowRuleInJarName is 'classes/FlowRule.json'
+ String flowRuleInJarPath = "FlowRule.json";
+
+ FileInJarReadableDataSource> flowRuleDataSource = new FileInJarReadableDataSource<>(
+ jarPath,flowRuleInJarPath, flowRuleListParser);
+ FlowRuleManager.register2Property(flowRuleDataSource.getProperty());
+ }
+
+ private Converter> flowRuleListParser = source -> JSON.parseObject(source,
+ new TypeReference>() {});
+}
diff --git a/sentinel-extension/sentinel-datasource-extension/src/main/java/com/alibaba/csp/sentinel/datasource/FileInJarReadableDataSource.java b/sentinel-extension/sentinel-datasource-extension/src/main/java/com/alibaba/csp/sentinel/datasource/FileInJarReadableDataSource.java
new file mode 100644
index 00000000..358653af
--- /dev/null
+++ b/sentinel-extension/sentinel-datasource-extension/src/main/java/com/alibaba/csp/sentinel/datasource/FileInJarReadableDataSource.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright 1999-2018 Alibaba Group Holding Ltd.
+ *
+ * Licensed 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 com.alibaba.csp.sentinel.datasource;
+
+import com.alibaba.csp.sentinel.log.RecordLog;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.Charset;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+/**
+ *
+ * A {@link ReadableDataSource} based on jarfile. This class can only read file when it
+ * run but will not automatically refresh if it is changed.
+ *
+ *
+ * Limitations: Default read buffer size is 1 MB. If file size is greater than
+ * buffer size, exceeding bytes will be ignored. Default charset is UTF-8.
+ *
+ *
+ * @author dingq
+ * @date 2019-03-30
+ */
+public class FileInJarReadableDataSource extends AutoRefreshDataSource {
+ private static final int MAX_SIZE = 1024 * 1024 * 4;
+ private static final long DEFAULT_REFRESH_MS = 3000;
+ private static final int DEFAULT_BUF_SIZE = 1024 * 1024;
+ private static final Charset DEFAULT_CHAR_SET = Charset.forName("utf-8");
+
+ private byte[] buf;
+ private JarEntry jarEntry;
+ private JarFile jarFile;
+ private final Charset charset;
+ private final String jarName;
+ private final String fileInJarName;
+
+ /**
+ * @param jarName the jar to read
+ * @param fileInJarName the file in jar to read
+ * @param configParser the config decoder (parser)
+ * @throws FileNotFoundException
+ */
+ public FileInJarReadableDataSource(String jarName, String fileInJarName, Converter configParser)
+ throws IOException {
+ this(jarName, fileInJarName, configParser, DEFAULT_REFRESH_MS, DEFAULT_BUF_SIZE, DEFAULT_CHAR_SET);
+ }
+
+ public FileInJarReadableDataSource(String jarName, String fileInJarName, Converter configParser, int bufSize)
+ throws IOException {
+ this(jarName, fileInJarName, configParser, DEFAULT_REFRESH_MS, bufSize, DEFAULT_CHAR_SET);
+ }
+
+ public FileInJarReadableDataSource(String jarName, String fileInJarName, Converter configParser, Charset charset)
+ throws IOException {
+ this(jarName, fileInJarName, configParser, DEFAULT_REFRESH_MS, DEFAULT_BUF_SIZE, charset);
+ }
+
+ public FileInJarReadableDataSource(String jarName, String fileInJarName, Converter configParser, long recommendRefreshMs, int bufSize,
+ Charset charset) throws IOException {
+ super(configParser, recommendRefreshMs);
+ if (bufSize <= 0 || bufSize > MAX_SIZE) {
+ throw new IllegalArgumentException("bufSize must between (0, " + MAX_SIZE + "], but " + bufSize + " get");
+ }
+ if (charset == null) {
+ throw new IllegalArgumentException("charset can't be null");
+ }
+ this.buf = new byte[bufSize];
+ this.charset = charset;
+ this.jarName = jarName;
+ this.fileInJarName = fileInJarName;
+ refreshJar();
+ firstLoad();
+ }
+
+ @Override
+ public String readSource() throws Exception {
+ if (null == jarEntry) {
+ // Will throw FileNotFoundException later.
+ RecordLog.warn(String.format("[FileInJarReadableDataSource] File does not exist: %s", jarFile.getName()));
+ }
+ InputStream inputStream = null;
+ try {
+ inputStream = jarFile.getInputStream(jarEntry);
+ if (inputStream.available() > buf.length) {
+ throw new IllegalStateException(jarFile.getName() + " file size=" + inputStream.available()
+ + ", is bigger than bufSize=" + buf.length + ". Can't read");
+ }
+ int len = inputStream.read(buf);
+ return new String(buf, 0, len, charset);
+ } finally {
+ if (inputStream != null) {
+ try {
+ inputStream.close();
+ } catch (Exception ignore) {
+ }
+ }
+ }
+
+ }
+
+ @Override
+ protected boolean isModified() {
+ return false;
+ }
+
+ private void firstLoad() {
+ try {
+ T newValue = loadConfig();
+ getProperty().updateValue(newValue);
+ } catch (Throwable e) {
+ RecordLog.info("loadConfig exception", e);
+ }
+ }
+
+ @Override
+ public void close() throws Exception {
+ super.close();
+ buf = null;
+ }
+
+ private void refreshJar() throws IOException {
+ this.jarFile = new JarFile(jarName);
+ this.jarEntry = jarFile.getJarEntry(fileInJarName);
+ }
+}