Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
Administrator
Arthas
Commits
5d7c4150
Commit
5d7c4150
authored
Dec 21, 2023
by
shengnan hu
Browse files
init
parents
Pipeline
#4715
failed with stage
in 30 seconds
Changes
457
Pipelines
620
Hide whitespace changes
Inline
Side-by-side
Showing
17 changed files
with
1976 additions
and
0 deletions
+1976
-0
core/src/main/java/com/taobao/arthas/core/distribution/impl/CompositeResultDistributorImpl.java
...ore/distribution/impl/CompositeResultDistributorImpl.java
+52
-0
core/src/main/java/com/taobao/arthas/core/distribution/impl/PackingResultDistributorImpl.java
.../core/distribution/impl/PackingResultDistributorImpl.java
+43
-0
core/src/main/java/com/taobao/arthas/core/distribution/impl/ResultConsumerImpl.java
...bao/arthas/core/distribution/impl/ResultConsumerImpl.java
+167
-0
core/src/main/java/com/taobao/arthas/core/distribution/impl/SharingResultDistributorImpl.java
.../core/distribution/impl/SharingResultDistributorImpl.java
+220
-0
core/src/main/java/com/taobao/arthas/core/distribution/impl/TermResultDistributorImpl.java
...has/core/distribution/impl/TermResultDistributorImpl.java
+36
-0
core/src/main/java/com/taobao/arthas/core/env/AbstractPropertyResolver.java
.../com/taobao/arthas/core/env/AbstractPropertyResolver.java
+228
-0
core/src/main/java/com/taobao/arthas/core/env/ArthasEnvironment.java
...in/java/com/taobao/arthas/core/env/ArthasEnvironment.java
+129
-0
core/src/main/java/com/taobao/arthas/core/env/ConfigurablePropertyResolver.java
.../taobao/arthas/core/env/ConfigurablePropertyResolver.java
+113
-0
core/src/main/java/com/taobao/arthas/core/env/ConversionService.java
...in/java/com/taobao/arthas/core/env/ConversionService.java
+63
-0
core/src/main/java/com/taobao/arthas/core/env/EnumerablePropertySource.java
.../com/taobao/arthas/core/env/EnumerablePropertySource.java
+85
-0
core/src/main/java/com/taobao/arthas/core/env/Environment.java
...src/main/java/com/taobao/arthas/core/env/Environment.java
+5
-0
core/src/main/java/com/taobao/arthas/core/env/MapPropertySource.java
...in/java/com/taobao/arthas/core/env/MapPropertySource.java
+52
-0
core/src/main/java/com/taobao/arthas/core/env/MissingRequiredPropertiesException.java
...o/arthas/core/env/MissingRequiredPropertiesException.java
+57
-0
core/src/main/java/com/taobao/arthas/core/env/MutablePropertySources.java
...va/com/taobao/arthas/core/env/MutablePropertySources.java
+304
-0
core/src/main/java/com/taobao/arthas/core/env/PropertiesPropertySource.java
.../com/taobao/arthas/core/env/PropertiesPropertySource.java
+56
-0
core/src/main/java/com/taobao/arthas/core/env/PropertyPlaceholderHelper.java
...com/taobao/arthas/core/env/PropertyPlaceholderHelper.java
+243
-0
core/src/main/java/com/taobao/arthas/core/env/PropertyResolver.java
...ain/java/com/taobao/arthas/core/env/PropertyResolver.java
+123
-0
No files found.
Too many changes to show.
To preserve performance only
457 of 457+
files are displayed.
Plain diff
Email patch
core/src/main/java/com/taobao/arthas/core/distribution/impl/CompositeResultDistributorImpl.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.distribution.impl
;
import
com.taobao.arthas.core.command.model.ResultModel
;
import
com.taobao.arthas.core.distribution.CompositeResultDistributor
;
import
com.taobao.arthas.core.distribution.ResultDistributor
;
import
java.util.ArrayList
;
import
java.util.Collections
;
import
java.util.List
;
/**
* 复合结果分发器,将消息同时分发给其包含的真实分发器
*
* @author gongdewei 2020/4/30
*/
public
class
CompositeResultDistributorImpl
implements
CompositeResultDistributor
{
private
List
<
ResultDistributor
>
distributors
=
Collections
.
synchronizedList
(
new
ArrayList
<
ResultDistributor
>());
public
CompositeResultDistributorImpl
()
{
}
public
CompositeResultDistributorImpl
(
ResultDistributor
...
distributors
)
{
for
(
ResultDistributor
distributor
:
distributors
)
{
this
.
addDistributor
(
distributor
);
}
}
@Override
public
void
addDistributor
(
ResultDistributor
distributor
)
{
distributors
.
add
(
distributor
);
}
@Override
public
void
removeDistributor
(
ResultDistributor
distributor
)
{
distributors
.
remove
(
distributor
);
}
@Override
public
void
appendResult
(
ResultModel
result
)
{
for
(
ResultDistributor
distributor
:
distributors
)
{
distributor
.
appendResult
(
result
);
}
}
@Override
public
void
close
()
{
for
(
ResultDistributor
distributor
:
distributors
)
{
distributor
.
close
();
}
}
}
core/src/main/java/com/taobao/arthas/core/distribution/impl/PackingResultDistributorImpl.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.distribution.impl
;
import
com.alibaba.arthas.deps.org.slf4j.Logger
;
import
com.alibaba.arthas.deps.org.slf4j.LoggerFactory
;
import
com.alibaba.fastjson.JSON
;
import
com.taobao.arthas.core.command.model.ResultModel
;
import
com.taobao.arthas.core.distribution.PackingResultDistributor
;
import
com.taobao.arthas.core.shell.session.Session
;
import
java.util.ArrayList
;
import
java.util.List
;
import
java.util.concurrent.ArrayBlockingQueue
;
import
java.util.concurrent.BlockingQueue
;
public
class
PackingResultDistributorImpl
implements
PackingResultDistributor
{
private
static
final
Logger
logger
=
LoggerFactory
.
getLogger
(
PackingResultDistributorImpl
.
class
);
private
BlockingQueue
<
ResultModel
>
resultQueue
=
new
ArrayBlockingQueue
<
ResultModel
>(
500
);
private
final
Session
session
;
public
PackingResultDistributorImpl
(
Session
session
)
{
this
.
session
=
session
;
}
@Override
public
void
appendResult
(
ResultModel
result
)
{
if
(!
resultQueue
.
offer
(
result
))
{
logger
.
warn
(
"result queue is full: {}, discard later result: {}"
,
resultQueue
.
size
(),
JSON
.
toJSONString
(
result
));
}
}
@Override
public
void
close
()
{
}
@Override
public
List
<
ResultModel
>
getResults
()
{
ArrayList
<
ResultModel
>
results
=
new
ArrayList
<
ResultModel
>(
resultQueue
.
size
());
resultQueue
.
drainTo
(
results
);
return
results
;
}
}
core/src/main/java/com/taobao/arthas/core/distribution/impl/ResultConsumerImpl.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.distribution.impl
;
import
com.alibaba.arthas.deps.org.slf4j.Logger
;
import
com.alibaba.arthas.deps.org.slf4j.LoggerFactory
;
import
com.alibaba.fastjson.JSON
;
import
com.taobao.arthas.core.command.model.ResultModel
;
import
com.taobao.arthas.core.distribution.DistributorOptions
;
import
com.taobao.arthas.core.distribution.ResultConsumer
;
import
com.taobao.arthas.core.distribution.ResultConsumerHelper
;
import
java.util.ArrayList
;
import
java.util.Collections
;
import
java.util.List
;
import
java.util.concurrent.ArrayBlockingQueue
;
import
java.util.concurrent.BlockingQueue
;
import
java.util.concurrent.TimeUnit
;
import
java.util.concurrent.locks.ReentrantLock
;
/**
* @author gongdewei 2020/3/27
*/
public
class
ResultConsumerImpl
implements
ResultConsumer
{
private
static
final
Logger
logger
=
LoggerFactory
.
getLogger
(
ResultConsumerImpl
.
class
);
private
BlockingQueue
<
ResultModel
>
resultQueue
;
private
volatile
long
lastAccessTime
;
private
volatile
boolean
polling
;
private
ReentrantLock
lock
=
new
ReentrantLock
();
private
int
resultBatchSizeLimit
=
20
;
private
int
resultQueueSize
=
DistributorOptions
.
resultQueueSize
;
private
long
pollTimeLimit
=
2
*
1000
;
private
String
consumerId
;
private
boolean
closed
;
private
long
sendingItemCount
;
public
ResultConsumerImpl
()
{
lastAccessTime
=
System
.
currentTimeMillis
();
resultQueue
=
new
ArrayBlockingQueue
<
ResultModel
>(
resultQueueSize
);
}
@Override
public
boolean
appendResult
(
ResultModel
result
)
{
//可能某些Consumer已经断开,不会再读取,这里不能堵塞!
boolean
discard
=
false
;
while
(!
resultQueue
.
offer
(
result
))
{
ResultModel
discardResult
=
resultQueue
.
poll
();
discard
=
true
;
}
return
!
discard
;
}
@Override
public
List
<
ResultModel
>
pollResults
()
{
try
{
lastAccessTime
=
System
.
currentTimeMillis
();
long
accessTime
=
lastAccessTime
;
if
(
lock
.
tryLock
(
500
,
TimeUnit
.
MILLISECONDS
))
{
polling
=
true
;
sendingItemCount
=
0
;
long
firstResultTime
=
0
;
// sending delay: time elapsed after firstResultTime
long
sendingDelay
=
0
;
// waiting time: time elapsed after access
long
waitingTime
=
0
;
List
<
ResultModel
>
sendingResults
=
new
ArrayList
<
ResultModel
>(
resultBatchSizeLimit
);
while
(!
closed
&&
sendingResults
.
size
()
<
resultBatchSizeLimit
&&
sendingDelay
<
100
&&
waitingTime
<
pollTimeLimit
)
{
ResultModel
aResult
=
resultQueue
.
poll
(
100
,
TimeUnit
.
MILLISECONDS
);
if
(
aResult
!=
null
)
{
sendingResults
.
add
(
aResult
);
//是否为第一次获取到数据
if
(
firstResultTime
==
0
)
{
firstResultTime
=
System
.
currentTimeMillis
();
}
//判断是否需要立即发送出去
if
(
shouldFlush
(
sendingResults
,
aResult
))
{
break
;
}
}
else
{
if
(
firstResultTime
>
0
)
{
//获取到部分数据后,队列已经取完,计算发送延时时间
sendingDelay
=
System
.
currentTimeMillis
()
-
firstResultTime
;
}
//计算总共等待时间,长轮询最大等待时间
waitingTime
=
System
.
currentTimeMillis
()
-
accessTime
;
}
}
//resultQueue.drainTo(sendingResults, resultSizeLimit-sendingResults.size());
if
(
logger
.
isDebugEnabled
())
{
logger
.
debug
(
"pollResults: {}, results: {}"
,
sendingResults
.
size
(),
JSON
.
toJSONString
(
sendingResults
));
}
return
sendingResults
;
}
}
catch
(
InterruptedException
e
)
{
//e.printStackTrace();
}
finally
{
if
(
lock
.
isHeldByCurrentThread
())
{
lastAccessTime
=
System
.
currentTimeMillis
();
polling
=
false
;
lock
.
unlock
();
}
}
return
Collections
.
emptyList
();
}
/**
* 估算对象数量及大小,判断是否需要立即发送出去
* @param sendingResults
* @param last
* @return
*/
private
boolean
shouldFlush
(
List
<
ResultModel
>
sendingResults
,
ResultModel
last
)
{
//TODO 引入一个估算模型,每个model自统计对象数量
sendingItemCount
+=
ResultConsumerHelper
.
getItemCount
(
last
);
return
sendingItemCount
>=
100
;
}
@Override
public
boolean
isHealthy
()
{
return
isPolling
()
||
resultQueue
.
size
()
<
resultQueueSize
||
System
.
currentTimeMillis
()
-
lastAccessTime
<
1000
;
}
@Override
public
long
getLastAccessTime
()
{
return
lastAccessTime
;
}
@Override
public
void
close
(){
this
.
closed
=
true
;
}
@Override
public
boolean
isClosed
()
{
return
closed
;
}
@Override
public
boolean
isPolling
()
{
return
polling
;
}
public
int
getResultBatchSizeLimit
()
{
return
resultBatchSizeLimit
;
}
public
void
setResultBatchSizeLimit
(
int
resultBatchSizeLimit
)
{
this
.
resultBatchSizeLimit
=
resultBatchSizeLimit
;
}
@Override
public
String
getConsumerId
()
{
return
consumerId
;
}
@Override
public
void
setConsumerId
(
String
consumerId
)
{
this
.
consumerId
=
consumerId
;
}
}
core/src/main/java/com/taobao/arthas/core/distribution/impl/SharingResultDistributorImpl.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.distribution.impl
;
import
com.alibaba.arthas.deps.org.slf4j.Logger
;
import
com.alibaba.arthas.deps.org.slf4j.LoggerFactory
;
import
com.taobao.arthas.core.command.model.InputStatusModel
;
import
com.taobao.arthas.core.command.model.MessageModel
;
import
com.taobao.arthas.core.command.model.ResultModel
;
import
com.taobao.arthas.core.distribution.DistributorOptions
;
import
com.taobao.arthas.core.distribution.ResultConsumer
;
import
com.taobao.arthas.core.distribution.SharingResultDistributor
;
import
com.taobao.arthas.core.shell.session.Session
;
import
com.taobao.arthas.core.shell.system.Job
;
import
java.util.List
;
import
java.util.UUID
;
import
java.util.concurrent.ArrayBlockingQueue
;
import
java.util.concurrent.BlockingQueue
;
import
java.util.concurrent.CopyOnWriteArrayList
;
import
java.util.concurrent.TimeUnit
;
import
java.util.concurrent.atomic.AtomicInteger
;
import
java.util.concurrent.locks.ReentrantLock
;
public
class
SharingResultDistributorImpl
implements
SharingResultDistributor
{
private
static
final
Logger
logger
=
LoggerFactory
.
getLogger
(
SharingResultDistributorImpl
.
class
);
private
List
<
ResultConsumer
>
consumers
=
new
CopyOnWriteArrayList
<
ResultConsumer
>();
private
BlockingQueue
<
ResultModel
>
pendingResultQueue
=
new
ArrayBlockingQueue
<
ResultModel
>(
10
);
private
final
Session
session
;
private
Thread
distributorThread
;
private
volatile
boolean
running
;
private
AtomicInteger
consumerNumGenerator
=
new
AtomicInteger
(
0
);
private
SharingResultConsumerImpl
sharingResultConsumer
=
new
SharingResultConsumerImpl
();
public
SharingResultDistributorImpl
(
Session
session
)
{
this
.
session
=
session
;
this
.
running
=
true
;
distributorThread
=
new
Thread
(
new
DistributorTask
(),
"ResultDistributor"
);
distributorThread
.
start
();
}
@Override
public
void
appendResult
(
ResultModel
result
)
{
//要避免阻塞影响业务线程执行
try
{
if
(!
pendingResultQueue
.
offer
(
result
,
100
,
TimeUnit
.
MILLISECONDS
))
{
ResultModel
discardResult
=
pendingResultQueue
.
poll
();
// 正常情况走不到这里,除非distribute 循环堵塞或异常终止
// 输出队列满,终止当前执行的命令
interruptJob
(
"result queue is full: "
+
pendingResultQueue
.
size
());
}
}
catch
(
InterruptedException
e
)
{
//ignore
}
}
private
void
interruptJob
(
String
message
)
{
Job
job
=
session
.
getForegroundJob
();
if
(
job
!=
null
)
{
logger
.
warn
(
message
+
", current job was interrupted."
,
job
.
id
());
job
.
interrupt
();
pendingResultQueue
.
offer
(
new
MessageModel
(
message
+
", current job was interrupted."
));
}
}
private
void
distribute
()
{
while
(
running
)
{
try
{
ResultModel
result
=
pendingResultQueue
.
poll
(
100
,
TimeUnit
.
MILLISECONDS
);
if
(
result
!=
null
)
{
sharingResultConsumer
.
appendResult
(
result
);
//判断是否有至少一个consumer是健康的
int
healthCount
=
0
;
for
(
int
i
=
0
;
i
<
consumers
.
size
();
i
++)
{
ResultConsumer
consumer
=
consumers
.
get
(
i
);
if
(
consumer
.
isHealthy
()){
healthCount
+=
1
;
}
consumer
.
appendResult
(
result
);
}
//所有consumer都不是健康状态,终止当前执行的命令
if
(
healthCount
==
0
)
{
interruptJob
(
"all consumers are unhealthy"
);
}
}
}
catch
(
Throwable
e
)
{
logger
.
warn
(
"distribute result failed: "
+
e
.
getMessage
(),
e
);
}
}
}
@Override
public
void
close
()
{
this
.
running
=
false
;
}
@Override
public
void
addConsumer
(
ResultConsumer
consumer
)
{
int
consumerNo
=
consumerNumGenerator
.
incrementAndGet
();
String
consumerId
=
UUID
.
randomUUID
().
toString
().
replaceAll
(
"-"
,
""
)
+
"_"
+
consumerNo
;
consumer
.
setConsumerId
(
consumerId
);
//将队列中的消息复制给新的消费者
sharingResultConsumer
.
copyTo
(
consumer
);
consumers
.
add
(
consumer
);
}
@Override
public
void
removeConsumer
(
ResultConsumer
consumer
)
{
consumers
.
remove
(
consumer
);
consumer
.
close
();
}
@Override
public
List
<
ResultConsumer
>
getConsumers
()
{
return
consumers
;
}
@Override
public
ResultConsumer
getConsumer
(
String
consumerId
)
{
for
(
int
i
=
0
;
i
<
consumers
.
size
();
i
++)
{
ResultConsumer
consumer
=
consumers
.
get
(
i
);
if
(
consumer
.
getConsumerId
().
equals
(
consumerId
))
{
return
consumer
;
}
}
return
null
;
}
private
class
DistributorTask
implements
Runnable
{
@Override
public
void
run
()
{
distribute
();
}
}
private
static
class
SharingResultConsumerImpl
implements
ResultConsumer
{
private
BlockingQueue
<
ResultModel
>
resultQueue
=
new
ArrayBlockingQueue
<
ResultModel
>(
DistributorOptions
.
resultQueueSize
);
private
ReentrantLock
queueLock
=
new
ReentrantLock
();
private
InputStatusModel
lastInputStatus
;
@Override
public
boolean
appendResult
(
ResultModel
result
)
{
queueLock
.
lock
();
try
{
//输入状态不入历史指令队列,复制时在最后发送
if
(
result
instanceof
InputStatusModel
)
{
lastInputStatus
=
(
InputStatusModel
)
result
;
return
true
;
}
while
(!
resultQueue
.
offer
(
result
))
{
ResultModel
discardResult
=
resultQueue
.
poll
();
}
}
finally
{
if
(
queueLock
.
isHeldByCurrentThread
())
{
queueLock
.
unlock
();
}
}
return
true
;
}
public
void
copyTo
(
ResultConsumer
consumer
)
{
//复制时加锁,避免消息顺序错乱,这里堵塞只影响分发线程,不会影响到业务线程
queueLock
.
lock
();
try
{
for
(
ResultModel
result
:
resultQueue
)
{
consumer
.
appendResult
(
result
);
}
//发送输入状态
if
(
lastInputStatus
!=
null
)
{
consumer
.
appendResult
(
lastInputStatus
);
}
}
finally
{
if
(
queueLock
.
isHeldByCurrentThread
())
{
queueLock
.
unlock
();
}
}
}
@Override
public
List
<
ResultModel
>
pollResults
()
{
return
null
;
}
@Override
public
long
getLastAccessTime
()
{
return
0
;
}
@Override
public
void
close
()
{
}
@Override
public
boolean
isClosed
()
{
return
false
;
}
@Override
public
boolean
isPolling
()
{
return
false
;
}
@Override
public
String
getConsumerId
()
{
return
"shared-consumer"
;
}
@Override
public
void
setConsumerId
(
String
consumerId
)
{
}
@Override
public
boolean
isHealthy
()
{
return
true
;
}
}
}
core/src/main/java/com/taobao/arthas/core/distribution/impl/TermResultDistributorImpl.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.distribution.impl
;
import
com.taobao.arthas.core.command.model.ResultModel
;
import
com.taobao.arthas.core.command.view.ResultView
;
import
com.taobao.arthas.core.command.view.ResultViewResolver
;
import
com.taobao.arthas.core.distribution.ResultDistributor
;
import
com.taobao.arthas.core.shell.command.CommandProcess
;
/**
* Term/Tty Result Distributor
*
* @author gongdewei 2020-03-26
*/
public
class
TermResultDistributorImpl
implements
ResultDistributor
{
private
final
CommandProcess
commandProcess
;
private
final
ResultViewResolver
resultViewResolver
;
public
TermResultDistributorImpl
(
CommandProcess
commandProcess
,
ResultViewResolver
resultViewResolver
)
{
this
.
commandProcess
=
commandProcess
;
this
.
resultViewResolver
=
resultViewResolver
;
}
@Override
public
void
appendResult
(
ResultModel
model
)
{
ResultView
resultView
=
resultViewResolver
.
getResultView
(
model
);
if
(
resultView
!=
null
)
{
resultView
.
draw
(
commandProcess
,
model
);
}
}
@Override
public
void
close
()
{
}
}
core/src/main/java/com/taobao/arthas/core/env/AbstractPropertyResolver.java
0 → 100644
View file @
5d7c4150
/*
* Copyright 2002-2018 the original author or authors.
*
* 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
*
* https://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.taobao.arthas.core.env
;
import
java.util.Arrays
;
import
java.util.LinkedHashSet
;
import
java.util.Set
;
import
com.taobao.arthas.core.env.convert.ConfigurableConversionService
;
import
com.taobao.arthas.core.env.convert.DefaultConversionService
;
/**
* Abstract base class for resolving properties against any underlying source.
*
* @author Chris Beams
* @author Juergen Hoeller
* @since 3.1
*/
public
abstract
class
AbstractPropertyResolver
implements
ConfigurablePropertyResolver
{
protected
ConfigurableConversionService
conversionService
=
new
DefaultConversionService
();
private
PropertyPlaceholderHelper
nonStrictHelper
;
private
PropertyPlaceholderHelper
strictHelper
;
private
boolean
ignoreUnresolvableNestedPlaceholders
=
false
;
private
String
placeholderPrefix
=
SystemPropertyUtils
.
PLACEHOLDER_PREFIX
;
private
String
placeholderSuffix
=
SystemPropertyUtils
.
PLACEHOLDER_SUFFIX
;
private
String
valueSeparator
=
SystemPropertyUtils
.
VALUE_SEPARATOR
;
private
final
Set
<
String
>
requiredProperties
=
new
LinkedHashSet
<
String
>();
public
ConfigurableConversionService
getConversionService
()
{
return
this
.
conversionService
;
}
public
void
setConversionService
(
ConfigurableConversionService
conversionService
)
{
this
.
conversionService
=
conversionService
;
}
/**
* Set the prefix that placeholders replaced by this resolver must begin with.
* <p>
* The default is "${".
*
* @see org.springframework.util.SystemPropertyUtils#PLACEHOLDER_PREFIX
*/
@Override
public
void
setPlaceholderPrefix
(
String
placeholderPrefix
)
{
this
.
placeholderPrefix
=
placeholderPrefix
;
}
/**
* Set the suffix that placeholders replaced by this resolver must end with.
* <p>
* The default is "}".
*
* @see org.springframework.util.SystemPropertyUtils#PLACEHOLDER_SUFFIX
*/
@Override
public
void
setPlaceholderSuffix
(
String
placeholderSuffix
)
{
this
.
placeholderSuffix
=
placeholderSuffix
;
}
/**
* Specify the separating character between the placeholders replaced by this
* resolver and their associated default value, or {@code null} if no such
* special character should be processed as a value separator.
* <p>
* The default is ":".
*
* @see org.springframework.util.SystemPropertyUtils#VALUE_SEPARATOR
*/
@Override
public
void
setValueSeparator
(
String
valueSeparator
)
{
this
.
valueSeparator
=
valueSeparator
;
}
/**
* Set whether to throw an exception when encountering an unresolvable
* placeholder nested within the value of a given property. A {@code false}
* value indicates strict resolution, i.e. that an exception will be thrown. A
* {@code true} value indicates that unresolvable nested placeholders should be
* passed through in their unresolved ${...} form.
* <p>
* The default is {@code false}.
*
* @since 3.2
*/
@Override
public
void
setIgnoreUnresolvableNestedPlaceholders
(
boolean
ignoreUnresolvableNestedPlaceholders
)
{
this
.
ignoreUnresolvableNestedPlaceholders
=
ignoreUnresolvableNestedPlaceholders
;
}
@Override
public
void
setRequiredProperties
(
String
...
requiredProperties
)
{
this
.
requiredProperties
.
addAll
(
Arrays
.
asList
(
requiredProperties
));
}
@Override
public
void
validateRequiredProperties
()
{
MissingRequiredPropertiesException
ex
=
new
MissingRequiredPropertiesException
();
for
(
String
key
:
this
.
requiredProperties
)
{
if
(
this
.
getProperty
(
key
)
==
null
)
{
ex
.
addMissingRequiredProperty
(
key
);
}
}
if
(!
ex
.
getMissingRequiredProperties
().
isEmpty
())
{
throw
ex
;
}
}
@Override
public
boolean
containsProperty
(
String
key
)
{
return
(
getProperty
(
key
)
!=
null
);
}
@Override
public
String
getProperty
(
String
key
)
{
return
getProperty
(
key
,
String
.
class
);
}
@Override
public
String
getProperty
(
String
key
,
String
defaultValue
)
{
String
value
=
getProperty
(
key
);
return
(
value
!=
null
?
value
:
defaultValue
);
}
@Override
public
<
T
>
T
getProperty
(
String
key
,
Class
<
T
>
targetType
,
T
defaultValue
)
{
T
value
=
getProperty
(
key
,
targetType
);
return
(
value
!=
null
?
value
:
defaultValue
);
}
@Override
public
String
getRequiredProperty
(
String
key
)
throws
IllegalStateException
{
String
value
=
getProperty
(
key
);
if
(
value
==
null
)
{
throw
new
IllegalStateException
(
"Required key '"
+
key
+
"' not found"
);
}
return
value
;
}
@Override
public
<
T
>
T
getRequiredProperty
(
String
key
,
Class
<
T
>
valueType
)
throws
IllegalStateException
{
T
value
=
getProperty
(
key
,
valueType
);
if
(
value
==
null
)
{
throw
new
IllegalStateException
(
"Required key '"
+
key
+
"' not found"
);
}
return
value
;
}
@Override
public
String
resolvePlaceholders
(
String
text
)
{
if
(
this
.
nonStrictHelper
==
null
)
{
this
.
nonStrictHelper
=
createPlaceholderHelper
(
true
);
}
return
doResolvePlaceholders
(
text
,
this
.
nonStrictHelper
);
}
@Override
public
String
resolveRequiredPlaceholders
(
String
text
)
throws
IllegalArgumentException
{
if
(
this
.
strictHelper
==
null
)
{
this
.
strictHelper
=
createPlaceholderHelper
(
false
);
}
return
doResolvePlaceholders
(
text
,
this
.
strictHelper
);
}
/**
* Resolve placeholders within the given string, deferring to the value of
* {@link #setIgnoreUnresolvableNestedPlaceholders} to determine whether any
* unresolvable placeholders should raise an exception or be ignored.
* <p>
* Invoked from {@link #getProperty} and its variants, implicitly resolving
* nested placeholders. In contrast, {@link #resolvePlaceholders} and
* {@link #resolveRequiredPlaceholders} do <i>not</i> delegate to this method
* but rather perform their own handling of unresolvable placeholders, as
* specified by each of those methods.
*
* @since 3.2
* @see #setIgnoreUnresolvableNestedPlaceholders
*/
protected
String
resolveNestedPlaceholders
(
String
value
)
{
return
(
this
.
ignoreUnresolvableNestedPlaceholders
?
resolvePlaceholders
(
value
)
:
resolveRequiredPlaceholders
(
value
));
}
private
PropertyPlaceholderHelper
createPlaceholderHelper
(
boolean
ignoreUnresolvablePlaceholders
)
{
return
new
PropertyPlaceholderHelper
(
this
.
placeholderPrefix
,
this
.
placeholderSuffix
,
this
.
valueSeparator
,
ignoreUnresolvablePlaceholders
);
}
private
String
doResolvePlaceholders
(
String
text
,
PropertyPlaceholderHelper
helper
)
{
return
helper
.
replacePlaceholders
(
text
,
new
PropertyPlaceholderHelper
.
PlaceholderResolver
()
{
public
String
resolvePlaceholder
(
String
placeholderName
)
{
return
getPropertyAsRawString
(
placeholderName
);
}
});
}
/**
* Retrieve the specified property as a raw String, i.e. without resolution of
* nested placeholders.
*
* @param key the property name to resolve
* @return the property value or {@code null} if none found
*/
protected
abstract
String
getPropertyAsRawString
(
String
key
);
}
core/src/main/java/com/taobao/arthas/core/env/ArthasEnvironment.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.env
;
import
java.security.AccessControlException
;
import
java.util.Map
;
/**
*
* @author hengyunabc 2019-12-27
*
*/
public
class
ArthasEnvironment
implements
Environment
{
/** System environment property source name: {@value}. */
public
static
final
String
SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME
=
"systemEnvironment"
;
/** JVM system properties property source name: {@value}. */
public
static
final
String
SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME
=
"systemProperties"
;
private
final
MutablePropertySources
propertySources
=
new
MutablePropertySources
();
private
final
ConfigurablePropertyResolver
propertyResolver
=
new
PropertySourcesPropertyResolver
(
this
.
propertySources
);
public
ArthasEnvironment
()
{
propertySources
.
addLast
(
new
PropertiesPropertySource
(
SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME
,
getSystemProperties
()));
propertySources
.
addLast
(
new
SystemEnvironmentPropertySource
(
SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME
,
getSystemEnvironment
()));
}
/**
* Add the given property source object with highest precedence.
*/
public
void
addFirst
(
PropertySource
<?>
propertySource
)
{
this
.
propertySources
.
addFirst
(
propertySource
);
}
/**
* Add the given property source object with lowest precedence.
*/
public
void
addLast
(
PropertySource
<?>
propertySource
)
{
this
.
propertySources
.
addLast
(
propertySource
);
}
@SuppressWarnings
({
"rawtypes"
,
"unchecked"
})
public
Map
<
String
,
Object
>
getSystemProperties
()
{
try
{
return
(
Map
)
System
.
getProperties
();
}
catch
(
AccessControlException
ex
)
{
return
(
Map
)
new
ReadOnlySystemAttributesMap
()
{
@Override
protected
String
getSystemAttribute
(
String
attributeName
)
{
try
{
return
System
.
getProperty
(
attributeName
);
}
catch
(
AccessControlException
ex
)
{
return
null
;
}
}
};
}
}
@SuppressWarnings
({
"rawtypes"
,
"unchecked"
})
public
Map
<
String
,
Object
>
getSystemEnvironment
()
{
try
{
return
(
Map
)
System
.
getenv
();
}
catch
(
AccessControlException
ex
)
{
return
(
Map
)
new
ReadOnlySystemAttributesMap
()
{
@Override
protected
String
getSystemAttribute
(
String
attributeName
)
{
try
{
return
System
.
getenv
(
attributeName
);
}
catch
(
AccessControlException
ex
)
{
return
null
;
}
}
};
}
}
// ---------------------------------------------------------------------
// Implementation of PropertyResolver interface
// ---------------------------------------------------------------------
@Override
public
boolean
containsProperty
(
String
key
)
{
return
this
.
propertyResolver
.
containsProperty
(
key
);
}
@Override
public
String
getProperty
(
String
key
)
{
return
this
.
propertyResolver
.
getProperty
(
key
);
}
@Override
public
String
getProperty
(
String
key
,
String
defaultValue
)
{
return
this
.
propertyResolver
.
getProperty
(
key
,
defaultValue
);
}
@Override
public
<
T
>
T
getProperty
(
String
key
,
Class
<
T
>
targetType
)
{
return
this
.
propertyResolver
.
getProperty
(
key
,
targetType
);
}
@Override
public
<
T
>
T
getProperty
(
String
key
,
Class
<
T
>
targetType
,
T
defaultValue
)
{
return
this
.
propertyResolver
.
getProperty
(
key
,
targetType
,
defaultValue
);
}
@Override
public
String
getRequiredProperty
(
String
key
)
throws
IllegalStateException
{
return
this
.
propertyResolver
.
getRequiredProperty
(
key
);
}
@Override
public
<
T
>
T
getRequiredProperty
(
String
key
,
Class
<
T
>
targetType
)
throws
IllegalStateException
{
return
this
.
propertyResolver
.
getRequiredProperty
(
key
,
targetType
);
}
@Override
public
String
resolvePlaceholders
(
String
text
)
{
return
this
.
propertyResolver
.
resolvePlaceholders
(
text
);
}
@Override
public
String
resolveRequiredPlaceholders
(
String
text
)
throws
IllegalArgumentException
{
return
this
.
propertyResolver
.
resolveRequiredPlaceholders
(
text
);
}
}
core/src/main/java/com/taobao/arthas/core/env/ConfigurablePropertyResolver.java
0 → 100644
View file @
5d7c4150
/*
* Copyright 2002-2016 the original author or authors.
*
* 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
*
* https://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.taobao.arthas.core.env
;
import
com.taobao.arthas.core.env.convert.ConfigurableConversionService
;
/**
* Configuration interface to be implemented by most if not all
* {@link PropertyResolver} types. Provides facilities for accessing and
* customizing the {@link org.springframework.core.convert.ConversionService
* ConversionService} used when converting property values from one type to
* another.
*
* @author Chris Beams
* @since 3.1
*/
public
interface
ConfigurablePropertyResolver
extends
PropertyResolver
{
/**
* Return the {@link ConfigurableConversionService} used when performing type
* conversions on properties.
* <p>
* The configurable nature of the returned conversion service allows for the
* convenient addition and removal of individual {@code Converter} instances:
*
* <pre class="code">
* ConfigurableConversionService cs = env.getConversionService();
* cs.addConverter(new FooConverter());
* </pre>
*
* @see PropertyResolver#getProperty(String, Class)
* @see org.springframework.core.convert.converter.ConverterRegistry#addConverter
*/
ConfigurableConversionService
getConversionService
();
/**
* Set the {@link ConfigurableConversionService} to be used when performing type
* conversions on properties.
* <p>
* <strong>Note:</strong> as an alternative to fully replacing the
* {@code ConversionService}, consider adding or removing individual
* {@code Converter} instances by drilling into {@link #getConversionService()}
* and calling methods such as {@code #addConverter}.
*
* @see PropertyResolver#getProperty(String, Class)
* @see #getConversionService()
* @see org.springframework.core.convert.converter.ConverterRegistry#addConverter
*/
void
setConversionService
(
ConfigurableConversionService
conversionService
);
/**
* Set the prefix that placeholders replaced by this resolver must begin with.
*/
void
setPlaceholderPrefix
(
String
placeholderPrefix
);
/**
* Set the suffix that placeholders replaced by this resolver must end with.
*/
void
setPlaceholderSuffix
(
String
placeholderSuffix
);
/**
* Specify the separating character between the placeholders replaced by this
* resolver and their associated default value, or {@code null} if no such
* special character should be processed as a value separator.
*/
void
setValueSeparator
(
String
valueSeparator
);
/**
* Set whether to throw an exception when encountering an unresolvable
* placeholder nested within the value of a given property. A {@code false}
* value indicates strict resolution, i.e. that an exception will be thrown. A
* {@code true} value indicates that unresolvable nested placeholders should be
* passed through in their unresolved ${...} form.
* <p>
* Implementations of {@link #getProperty(String)} and its variants must inspect
* the value set here to determine correct behavior when property values contain
* unresolvable placeholders.
*
* @since 3.2
*/
void
setIgnoreUnresolvableNestedPlaceholders
(
boolean
ignoreUnresolvableNestedPlaceholders
);
/**
* Specify which properties must be present, to be verified by
* {@link #validateRequiredProperties()}.
*/
void
setRequiredProperties
(
String
...
requiredProperties
);
/**
* Validate that each of the properties specified by
* {@link #setRequiredProperties} is present and resolves to a non-{@code null}
* value.
*
* @throws MissingRequiredPropertiesException if any of the required properties
* are not resolvable.
*/
void
validateRequiredProperties
()
throws
MissingRequiredPropertiesException
;
}
core/src/main/java/com/taobao/arthas/core/env/ConversionService.java
0 → 100644
View file @
5d7c4150
/*
* Copyright 2002-2016 the original author or authors.
*
* 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.taobao.arthas.core.env
;
/**
* A service interface for type conversion. This is the entry point into the
* convert system. Call {@link #convert(Object, Class)} to perform a thread-safe
* type conversion using this system.
*
* @author Keith Donald
* @author Phillip Webb
* @since 3.0
*/
public
interface
ConversionService
{
/**
* Return {@code true} if objects of {@code sourceType} can be converted to the
* {@code targetType}.
* <p>
* If this method returns {@code true}, it means {@link #convert(Object, Class)}
* is capable of converting an instance of {@code sourceType} to
* {@code targetType}.
* <p>
* Special note on collections, arrays, and maps types: For conversion between
* collection, array, and map types, this method will return {@code true} even
* though a convert invocation may still generate a {@link ConversionException}
* if the underlying elements are not convertible. Callers are expected to
* handle this exceptional case when working with collections and maps.
*
* @param sourceType the source type to convert from (may be {@code null} if
* source is {@code null})
* @param targetType the target type to convert to (required)
* @return {@code true} if a conversion can be performed, {@code false} if not
* @throws IllegalArgumentException if {@code targetType} is {@code null}
*/
boolean
canConvert
(
Class
<?>
sourceType
,
Class
<?>
targetType
);
/**
* Convert the given {@code source} to the specified {@code targetType}.
*
* @param source the source object to convert (may be {@code null})
* @param targetType the target type to convert to (required)
* @return the converted object, an instance of targetType
* @throws ConversionException if a conversion exception occurred
* @throws IllegalArgumentException if targetType is {@code null}
*/
<
T
>
T
convert
(
Object
source
,
Class
<
T
>
targetType
);
}
core/src/main/java/com/taobao/arthas/core/env/EnumerablePropertySource.java
0 → 100644
View file @
5d7c4150
/*
* Copyright 2002-2018 the original author or authors.
*
* 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
*
* https://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.taobao.arthas.core.env
;
/**
* A {@link PropertySource} implementation capable of interrogating its
* underlying source object to enumerate all possible property name/value pairs.
* Exposes the {@link #getPropertyNames()} method to allow callers to introspect
* available properties without having to access the underlying source object.
* This also facilitates a more efficient implementation of
* {@link #containsProperty(String)}, in that it can call
* {@link #getPropertyNames()} and iterate through the returned array rather
* than attempting a call to {@link #getProperty(String)} which may be more
* expensive. Implementations may consider caching the result of
* {@link #getPropertyNames()} to fully exploit this performance opportunity.
*
* <p>
* Most framework-provided {@code PropertySource} implementations are
* enumerable; a counter-example would be {@code JndiPropertySource} where, due
* to the nature of JNDI it is not possible to determine all possible property
* names at any given time; rather it is only possible to try to access a
* property (via {@link #getProperty(String)}) in order to evaluate whether it
* is present or not.
*
* @author Chris Beams
* @author Juergen Hoeller
* @since 3.1
* @param <T> the source type
*/
public
abstract
class
EnumerablePropertySource
<
T
>
extends
PropertySource
<
T
>
{
public
EnumerablePropertySource
(
String
name
,
T
source
)
{
super
(
name
,
source
);
}
protected
EnumerablePropertySource
(
String
name
)
{
super
(
name
);
}
/**
* Return whether this {@code PropertySource} contains a property with the given
* name.
* <p>
* This implementation checks for the presence of the given name within the
* {@link #getPropertyNames()} array.
*
* @param name the name of the property to find
*/
@Override
public
boolean
containsProperty
(
String
name
)
{
String
[]
propertyNames
=
getPropertyNames
();
if
(
propertyNames
==
null
)
{
return
false
;
}
for
(
String
temp
:
propertyNames
)
{
if
(
temp
.
equals
(
name
))
{
return
true
;
}
}
return
false
;
}
/**
* Return the names of all properties contained by the {@linkplain #getSource()
* source} object (never {@code null}).
*/
public
abstract
String
[]
getPropertyNames
();
}
core/src/main/java/com/taobao/arthas/core/env/Environment.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.env
;
public
interface
Environment
extends
PropertyResolver
{
}
core/src/main/java/com/taobao/arthas/core/env/MapPropertySource.java
0 → 100644
View file @
5d7c4150
/*
* Copyright 2002-2014 the original author or authors.
*
* 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
*
* https://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.taobao.arthas.core.env
;
import
java.util.Map
;
import
org.apache.logging.log4j.util.PropertiesPropertySource
;
/**
* {@link PropertySource} that reads keys and values from a {@code Map} object.
*
* @author Chris Beams
* @author Juergen Hoeller
* @since 3.1
* @see PropertiesPropertySource
*/
public
class
MapPropertySource
extends
EnumerablePropertySource
<
Map
<
String
,
Object
>>
{
public
MapPropertySource
(
String
name
,
Map
<
String
,
Object
>
source
)
{
super
(
name
,
source
);
}
@Override
public
Object
getProperty
(
String
name
)
{
return
this
.
source
.
get
(
name
);
}
@Override
public
boolean
containsProperty
(
String
name
)
{
return
this
.
source
.
containsKey
(
name
);
}
@Override
public
String
[]
getPropertyNames
()
{
return
this
.
source
.
keySet
().
toArray
(
new
String
[
0
]);
}
}
core/src/main/java/com/taobao/arthas/core/env/MissingRequiredPropertiesException.java
0 → 100644
View file @
5d7c4150
/*
* Copyright 2002-2017 the original author or authors.
*
* 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
*
* https://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.taobao.arthas.core.env
;
import
java.util.LinkedHashSet
;
import
java.util.Set
;
/**
* Exception thrown when required properties are not found.
*
* @author Chris Beams
* @since 3.1
* @see ConfigurablePropertyResolver#setRequiredProperties(String...)
* @see ConfigurablePropertyResolver#validateRequiredProperties()
* @see org.springframework.context.support.AbstractApplicationContext#prepareRefresh()
*/
@SuppressWarnings
(
"serial"
)
public
class
MissingRequiredPropertiesException
extends
IllegalStateException
{
private
final
Set
<
String
>
missingRequiredProperties
=
new
LinkedHashSet
<
String
>();
void
addMissingRequiredProperty
(
String
key
)
{
this
.
missingRequiredProperties
.
add
(
key
);
}
@Override
public
String
getMessage
()
{
return
"The following properties were declared as required but could not be resolved: "
+
getMissingRequiredProperties
();
}
/**
* Return the set of properties marked as required but not present upon
* validation.
*
* @see ConfigurablePropertyResolver#setRequiredProperties(String...)
* @see ConfigurablePropertyResolver#validateRequiredProperties()
*/
public
Set
<
String
>
getMissingRequiredProperties
()
{
return
this
.
missingRequiredProperties
;
}
}
core/src/main/java/com/taobao/arthas/core/env/MutablePropertySources.java
0 → 100644
View file @
5d7c4150
/*
* Copyright 2002-2014 the original author or authors.
*
* 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.taobao.arthas.core.env
;
import
java.util.Iterator
;
import
java.util.LinkedList
;
/**
* Default implementation of the {@link PropertySources} interface. Allows
* manipulation of contained property sources and provides a constructor for
* copying an existing {@code PropertySources} instance.
*
* <p>
* Where <em>precedence</em> is mentioned in methods such as {@link #addFirst}
* and {@link #addLast}, this is with regard to the order in which property
* sources will be searched when resolving a given property with a
* {@link PropertyResolver}.
*
* @author Chris Beams
* @since 3.1
* @see PropertySourcesPropertyResolver
*/
public
class
MutablePropertySources
implements
PropertySources
{
static
final
String
NON_EXISTENT_PROPERTY_SOURCE_MESSAGE
=
"PropertySource named [%s] does not exist"
;
static
final
String
ILLEGAL_RELATIVE_ADDITION_MESSAGE
=
"PropertySource named [%s] cannot be added relative to itself"
;
private
final
LinkedList
<
PropertySource
<?>>
propertySourceList
=
new
LinkedList
<
PropertySource
<?>>();
/**
* Create a new {@link MutablePropertySources} object.
*/
public
MutablePropertySources
()
{
}
/**
* Create a new {@code MutablePropertySources} from the given propertySources
* object, preserving the original order of contained {@code PropertySource}
* objects.
*/
public
MutablePropertySources
(
PropertySources
propertySources
)
{
this
();
for
(
PropertySource
<?>
propertySource
:
propertySources
)
{
this
.
addLast
(
propertySource
);
}
}
public
boolean
contains
(
String
name
)
{
return
this
.
propertySourceList
.
contains
(
PropertySource
.
named
(
name
));
}
public
PropertySource
<?>
get
(
String
name
)
{
int
index
=
this
.
propertySourceList
.
indexOf
(
PropertySource
.
named
(
name
));
return
index
==
-
1
?
null
:
this
.
propertySourceList
.
get
(
index
);
}
public
Iterator
<
PropertySource
<?>>
iterator
()
{
return
this
.
propertySourceList
.
iterator
();
}
/**
* Add the given property source object with highest precedence.
*/
public
void
addFirst
(
PropertySource
<?>
propertySource
)
{
// if (logger.isDebugEnabled()) {
// logger.debug(String.format("Adding [%s] PropertySource with highest search precedence",
// propertySource.getName()));
// }
removeIfPresent
(
propertySource
);
this
.
propertySourceList
.
addFirst
(
propertySource
);
}
/**
* Add the given property source object with lowest precedence.
*/
public
void
addLast
(
PropertySource
<?>
propertySource
)
{
// if (logger.isDebugEnabled()) {
// logger.debug(String.format("Adding [%s] PropertySource with lowest search precedence",
// propertySource.getName()));
// }
removeIfPresent
(
propertySource
);
this
.
propertySourceList
.
addLast
(
propertySource
);
}
/**
* Add the given property source object with precedence immediately higher than
* the named relative property source.
*/
public
void
addBefore
(
String
relativePropertySourceName
,
PropertySource
<?>
propertySource
)
{
// if (logger.isDebugEnabled()) {
// logger.debug(String.format("Adding [%s] PropertySource with search precedence immediately higher than [%s]",
// propertySource.getName(), relativePropertySourceName));
// }
assertLegalRelativeAddition
(
relativePropertySourceName
,
propertySource
);
removeIfPresent
(
propertySource
);
int
index
=
assertPresentAndGetIndex
(
relativePropertySourceName
);
addAtIndex
(
index
,
propertySource
);
}
/**
* Add the given property source object with precedence immediately lower than
* the named relative property source.
*/
public
void
addAfter
(
String
relativePropertySourceName
,
PropertySource
<?>
propertySource
)
{
// if (logger.isDebugEnabled()) {
// logger.debug(String.format("Adding [%s] PropertySource with search precedence immediately lower than [%s]",
// propertySource.getName(), relativePropertySourceName));
// }
assertLegalRelativeAddition
(
relativePropertySourceName
,
propertySource
);
removeIfPresent
(
propertySource
);
int
index
=
assertPresentAndGetIndex
(
relativePropertySourceName
);
addAtIndex
(
index
+
1
,
propertySource
);
}
/**
* Return the precedence of the given property source, {@code -1} if not found.
*/
public
int
precedenceOf
(
PropertySource
<?>
propertySource
)
{
return
this
.
propertySourceList
.
indexOf
(
propertySource
);
}
/**
* Remove and return the property source with the given name, {@code null} if
* not found.
*
* @param name the name of the property source to find and remove
*/
public
PropertySource
<?>
remove
(
String
name
)
{
// if (logger.isDebugEnabled()) {
// logger.debug(String.format("Removing [%s] PropertySource", name));
// }
int
index
=
this
.
propertySourceList
.
indexOf
(
PropertySource
.
named
(
name
));
return
index
==
-
1
?
null
:
this
.
propertySourceList
.
remove
(
index
);
}
/**
* Replace the property source with the given name with the given property
* source object.
*
* @param name the name of the property source to find and replace
* @param propertySource the replacement property source
* @throws IllegalArgumentException if no property source with the given name is
* present
* @see #contains
*/
public
void
replace
(
String
name
,
PropertySource
<?>
propertySource
)
{
// if (logger.isDebugEnabled()) {
// logger.debug(String.format("Replacing [%s] PropertySource with [%s]",
// name, propertySource.getName()));
// }
int
index
=
assertPresentAndGetIndex
(
name
);
this
.
propertySourceList
.
set
(
index
,
propertySource
);
}
/**
* Return the number of {@link PropertySource} objects contained.
*/
public
int
size
()
{
return
this
.
propertySourceList
.
size
();
}
@Override
public
String
toString
()
{
String
[]
names
=
new
String
[
this
.
size
()];
for
(
int
i
=
0
;
i
<
size
();
i
++)
{
names
[
i
]
=
this
.
propertySourceList
.
get
(
i
).
getName
();
}
return
String
.
format
(
"[%s]"
,
arrayToCommaDelimitedString
(
names
));
}
/**
* Ensure that the given property source is not being added relative to itself.
*/
protected
void
assertLegalRelativeAddition
(
String
relativePropertySourceName
,
PropertySource
<?>
propertySource
)
{
// String newPropertySourceName = propertySource.getName();
// Assert.isTrue(!relativePropertySourceName.equals(newPropertySourceName),
// String.format(ILLEGAL_RELATIVE_ADDITION_MESSAGE, newPropertySourceName));
}
/**
* Remove the given property source if it is present.
*/
protected
void
removeIfPresent
(
PropertySource
<?>
propertySource
)
{
this
.
propertySourceList
.
remove
(
propertySource
);
}
/**
* Add the given property source at a particular index in the list.
*/
private
void
addAtIndex
(
int
index
,
PropertySource
<?>
propertySource
)
{
removeIfPresent
(
propertySource
);
this
.
propertySourceList
.
add
(
index
,
propertySource
);
}
/**
* Assert that the named property source is present and return its index.
*
* @param name the {@linkplain PropertySource#getName() name of the property
* source} to find
* @throws IllegalArgumentException if the named property source is not present
*/
private
int
assertPresentAndGetIndex
(
String
name
)
{
int
index
=
this
.
propertySourceList
.
indexOf
(
PropertySource
.
named
(
name
));
// Assert.isTrue(index >= 0, String.format(NON_EXISTENT_PROPERTY_SOURCE_MESSAGE, name));
return
index
;
}
/**
* Convenience method to return a String array as a delimited (e.g. CSV) String.
* E.g. useful for {@code toString()} implementations.
*
* @param arr the array to display
* @param delim the delimiter to use (probably a ",")
* @return the delimited String
*/
private
static
String
arrayToDelimitedString
(
Object
[]
arr
,
String
delim
)
{
if
(
arr
==
null
||
arr
.
length
==
0
)
{
return
""
;
}
if
(
arr
.
length
==
1
)
{
return
nullSafeToString
(
arr
[
0
]);
}
StringBuilder
sb
=
new
StringBuilder
();
for
(
int
i
=
0
;
i
<
arr
.
length
;
i
++)
{
if
(
i
>
0
)
{
sb
.
append
(
delim
);
}
sb
.
append
(
arr
[
i
]);
}
return
sb
.
toString
();
}
/**
* Return a String representation of the specified Object.
* <p>
* Builds a String representation of the contents in case of an array. Returns
* {@code "null"} if {@code obj} is {@code null}.
*
* @param obj the object to build a String representation for
* @return a String representation of {@code obj}
*/
private
static
String
nullSafeToString
(
Object
obj
)
{
if
(
obj
==
null
)
{
return
"null"
;
}
if
(
obj
instanceof
String
)
{
return
(
String
)
obj
;
}
if
(
obj
instanceof
Object
[])
{
return
nullSafeToString
((
Object
[])
obj
);
}
if
(
obj
instanceof
boolean
[])
{
return
nullSafeToString
((
boolean
[])
obj
);
}
if
(
obj
instanceof
byte
[])
{
return
nullSafeToString
((
byte
[])
obj
);
}
if
(
obj
instanceof
char
[])
{
return
nullSafeToString
((
char
[])
obj
);
}
if
(
obj
instanceof
double
[])
{
return
nullSafeToString
((
double
[])
obj
);
}
if
(
obj
instanceof
float
[])
{
return
nullSafeToString
((
float
[])
obj
);
}
if
(
obj
instanceof
int
[])
{
return
nullSafeToString
((
int
[])
obj
);
}
if
(
obj
instanceof
long
[])
{
return
nullSafeToString
((
long
[])
obj
);
}
if
(
obj
instanceof
short
[])
{
return
nullSafeToString
((
short
[])
obj
);
}
String
str
=
obj
.
toString
();
return
(
str
!=
null
?
str
:
""
);
}
/**
* Convenience method to return a String array as a CSV String. E.g. useful for
* {@code toString()} implementations.
*
* @param arr the array to display
* @return the delimited String
*/
private
static
String
arrayToCommaDelimitedString
(
Object
[]
arr
)
{
return
arrayToDelimitedString
(
arr
,
","
);
}
}
core/src/main/java/com/taobao/arthas/core/env/PropertiesPropertySource.java
0 → 100644
View file @
5d7c4150
/*
* Copyright 2002-2019 the original author or authors.
*
* 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
*
* https://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.taobao.arthas.core.env
;
import
java.util.Map
;
import
java.util.Properties
;
/**
* {@link PropertySource} implementation that extracts properties from a
* {@link java.util.Properties} object.
*
* <p>
* Note that because a {@code Properties} object is technically an
* {@code <Object, Object>} {@link java.util.Hashtable Hashtable}, one may
* contain non-{@code String} keys or values. This implementation, however is
* restricted to accessing only {@code String}-based keys and values, in the
* same fashion as {@link Properties#getProperty} and
* {@link Properties#setProperty}.
*
* @author Chris Beams
* @author Juergen Hoeller
* @since 3.1
*/
public
class
PropertiesPropertySource
extends
MapPropertySource
{
@SuppressWarnings
({
"rawtypes"
,
"unchecked"
})
public
PropertiesPropertySource
(
String
name
,
Properties
source
)
{
super
(
name
,
(
Map
)
source
);
}
protected
PropertiesPropertySource
(
String
name
,
Map
<
String
,
Object
>
source
)
{
super
(
name
,
source
);
}
@Override
public
String
[]
getPropertyNames
()
{
synchronized
(
this
.
source
)
{
return
super
.
getPropertyNames
();
}
}
}
core/src/main/java/com/taobao/arthas/core/env/PropertyPlaceholderHelper.java
0 → 100644
View file @
5d7c4150
/*
* Copyright 2002-2019 the original author or authors.
*
* 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
*
* https://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.taobao.arthas.core.env
;
import
java.util.HashMap
;
import
java.util.HashSet
;
import
java.util.Map
;
import
java.util.Properties
;
import
java.util.Set
;
/**
* Utility class for working with Strings that have placeholder values in them.
* A placeholder takes the form {@code ${name}}. Using
* {@code PropertyPlaceholderHelper} these placeholders can be substituted for
* user-supplied values.
* <p>
* Values for substitution can be supplied using a {@link Properties} instance
* or using a {@link PlaceholderResolver}.
*
* @author Juergen Hoeller
* @author Rob Harrop
* @since 3.0
*/
public
class
PropertyPlaceholderHelper
{
private
static
final
Map
<
String
,
String
>
wellKnownSimplePrefixes
=
new
HashMap
<
String
,
String
>(
4
);
static
{
wellKnownSimplePrefixes
.
put
(
"}"
,
"{"
);
wellKnownSimplePrefixes
.
put
(
"]"
,
"["
);
wellKnownSimplePrefixes
.
put
(
")"
,
"("
);
}
private
final
String
placeholderPrefix
;
private
final
String
placeholderSuffix
;
private
final
String
simplePrefix
;
private
final
String
valueSeparator
;
private
final
boolean
ignoreUnresolvablePlaceholders
;
/**
* Creates a new {@code PropertyPlaceholderHelper} that uses the supplied prefix
* and suffix. Unresolvable placeholders are ignored.
*
* @param placeholderPrefix the prefix that denotes the start of a placeholder
* @param placeholderSuffix the suffix that denotes the end of a placeholder
*/
public
PropertyPlaceholderHelper
(
String
placeholderPrefix
,
String
placeholderSuffix
)
{
this
(
placeholderPrefix
,
placeholderSuffix
,
null
,
true
);
}
/**
* Creates a new {@code PropertyPlaceholderHelper} that uses the supplied prefix
* and suffix.
*
* @param placeholderPrefix the prefix that denotes the start of a
* placeholder
* @param placeholderSuffix the suffix that denotes the end of a
* placeholder
* @param valueSeparator the separating character between the
* placeholder variable and the associated
* default value, if any
* @param ignoreUnresolvablePlaceholders indicates whether unresolvable
* placeholders should be ignored
* ({@code true}) or cause an exception
* ({@code false})
*/
public
PropertyPlaceholderHelper
(
String
placeholderPrefix
,
String
placeholderSuffix
,
String
valueSeparator
,
boolean
ignoreUnresolvablePlaceholders
)
{
this
.
placeholderPrefix
=
placeholderPrefix
;
this
.
placeholderSuffix
=
placeholderSuffix
;
String
simplePrefixForSuffix
=
wellKnownSimplePrefixes
.
get
(
this
.
placeholderSuffix
);
if
(
simplePrefixForSuffix
!=
null
&&
this
.
placeholderPrefix
.
endsWith
(
simplePrefixForSuffix
))
{
this
.
simplePrefix
=
simplePrefixForSuffix
;
}
else
{
this
.
simplePrefix
=
this
.
placeholderPrefix
;
}
this
.
valueSeparator
=
valueSeparator
;
this
.
ignoreUnresolvablePlaceholders
=
ignoreUnresolvablePlaceholders
;
}
/**
* Replaces all placeholders of format {@code ${name}} with the corresponding
* property from the supplied {@link Properties}.
*
* @param value the value containing the placeholders to be replaced
* @param properties the {@code Properties} to use for replacement
* @return the supplied value with placeholders replaced inline
*/
public
String
replacePlaceholders
(
String
value
,
final
Properties
properties
)
{
return
replacePlaceholders
(
value
,
new
PlaceholderResolver
()
{
public
String
resolvePlaceholder
(
String
placeholderName
)
{
return
properties
.
getProperty
(
placeholderName
);
}
});
}
/**
* Replaces all placeholders of format {@code ${name}} with the value returned
* from the supplied {@link PlaceholderResolver}.
*
* @param value the value containing the placeholders to be
* replaced
* @param placeholderResolver the {@code PlaceholderResolver} to use for
* replacement
* @return the supplied value with placeholders replaced inline
*/
public
String
replacePlaceholders
(
String
value
,
PlaceholderResolver
placeholderResolver
)
{
return
parseStringValue
(
value
,
placeholderResolver
,
null
);
}
protected
String
parseStringValue
(
String
value
,
PlaceholderResolver
placeholderResolver
,
Set
<
String
>
visitedPlaceholders
)
{
int
startIndex
=
value
.
indexOf
(
this
.
placeholderPrefix
);
if
(
startIndex
==
-
1
)
{
return
value
;
}
StringBuilder
result
=
new
StringBuilder
(
value
);
while
(
startIndex
!=
-
1
)
{
int
endIndex
=
findPlaceholderEndIndex
(
result
,
startIndex
);
if
(
endIndex
!=
-
1
)
{
String
placeholder
=
result
.
substring
(
startIndex
+
this
.
placeholderPrefix
.
length
(),
endIndex
);
String
originalPlaceholder
=
placeholder
;
if
(
visitedPlaceholders
==
null
)
{
visitedPlaceholders
=
new
HashSet
<
String
>(
4
);
}
if
(!
visitedPlaceholders
.
add
(
originalPlaceholder
))
{
throw
new
IllegalArgumentException
(
"Circular placeholder reference '"
+
originalPlaceholder
+
"' in property definitions"
);
}
// Recursive invocation, parsing placeholders contained in the placeholder key.
placeholder
=
parseStringValue
(
placeholder
,
placeholderResolver
,
visitedPlaceholders
);
// Now obtain the value for the fully resolved key...
String
propVal
=
placeholderResolver
.
resolvePlaceholder
(
placeholder
);
if
(
propVal
==
null
&&
this
.
valueSeparator
!=
null
)
{
int
separatorIndex
=
placeholder
.
indexOf
(
this
.
valueSeparator
);
if
(
separatorIndex
!=
-
1
)
{
String
actualPlaceholder
=
placeholder
.
substring
(
0
,
separatorIndex
);
String
defaultValue
=
placeholder
.
substring
(
separatorIndex
+
this
.
valueSeparator
.
length
());
propVal
=
placeholderResolver
.
resolvePlaceholder
(
actualPlaceholder
);
if
(
propVal
==
null
)
{
propVal
=
defaultValue
;
}
}
}
if
(
propVal
!=
null
)
{
// Recursive invocation, parsing placeholders contained in the
// previously resolved placeholder value.
propVal
=
parseStringValue
(
propVal
,
placeholderResolver
,
visitedPlaceholders
);
result
.
replace
(
startIndex
,
endIndex
+
this
.
placeholderSuffix
.
length
(),
propVal
);
startIndex
=
result
.
indexOf
(
this
.
placeholderPrefix
,
startIndex
+
propVal
.
length
());
}
else
if
(
this
.
ignoreUnresolvablePlaceholders
)
{
// Proceed with unprocessed value.
startIndex
=
result
.
indexOf
(
this
.
placeholderPrefix
,
endIndex
+
this
.
placeholderSuffix
.
length
());
}
else
{
throw
new
IllegalArgumentException
(
"Could not resolve placeholder '"
+
placeholder
+
"'"
+
" in value \""
+
value
+
"\""
);
}
visitedPlaceholders
.
remove
(
originalPlaceholder
);
}
else
{
startIndex
=
-
1
;
}
}
return
result
.
toString
();
}
private
int
findPlaceholderEndIndex
(
CharSequence
buf
,
int
startIndex
)
{
int
index
=
startIndex
+
this
.
placeholderPrefix
.
length
();
int
withinNestedPlaceholder
=
0
;
while
(
index
<
buf
.
length
())
{
if
(
substringMatch
(
buf
,
index
,
this
.
placeholderSuffix
))
{
if
(
withinNestedPlaceholder
>
0
)
{
withinNestedPlaceholder
--;
index
=
index
+
this
.
placeholderSuffix
.
length
();
}
else
{
return
index
;
}
}
else
if
(
substringMatch
(
buf
,
index
,
this
.
simplePrefix
))
{
withinNestedPlaceholder
++;
index
=
index
+
this
.
simplePrefix
.
length
();
}
else
{
index
++;
}
}
return
-
1
;
}
/**
* Test whether the given string matches the given substring at the given index.
*
* @param str the original string (or StringBuilder)
* @param index the index in the original string to start matching against
* @param substring the substring to match at the given index
*/
public
static
boolean
substringMatch
(
CharSequence
str
,
int
index
,
CharSequence
substring
)
{
if
(
index
+
substring
.
length
()
>
str
.
length
())
{
return
false
;
}
for
(
int
i
=
0
;
i
<
substring
.
length
();
i
++)
{
if
(
str
.
charAt
(
index
+
i
)
!=
substring
.
charAt
(
i
))
{
return
false
;
}
}
return
true
;
}
/**
* Strategy interface used to resolve replacement values for placeholders
* contained in Strings.
*/
public
interface
PlaceholderResolver
{
/**
* Resolve the supplied placeholder name to the replacement value.
*
* @param placeholderName the name of the placeholder to resolve
* @return the replacement value, or {@code null} if no replacement is to be
* made
*/
String
resolvePlaceholder
(
String
placeholderName
);
}
}
core/src/main/java/com/taobao/arthas/core/env/PropertyResolver.java
0 → 100644
View file @
5d7c4150
/*
* Copyright 2002-2016 the original author or authors.
*
* 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
*
* https://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.taobao.arthas.core.env
;
/**
* Interface for resolving properties against any underlying source.
*
* @author Chris Beams
* @author Juergen Hoeller
* @since 3.1
* @see Environment
* @see PropertySourcesPropertyResolver
*/
public
interface
PropertyResolver
{
/**
* Return whether the given property key is available for resolution, i.e. if
* the value for the given key is not {@code null}.
*/
boolean
containsProperty
(
String
key
);
/**
* Return the property value associated with the given key, or {@code null} if
* the key cannot be resolved.
*
* @param key the property name to resolve
* @see #getProperty(String, String)
* @see #getProperty(String, Class)
* @see #getRequiredProperty(String)
*/
String
getProperty
(
String
key
);
/**
* Return the property value associated with the given key, or
* {@code defaultValue} if the key cannot be resolved.
*
* @param key the property name to resolve
* @param defaultValue the default value to return if no value is found
* @see #getRequiredProperty(String)
* @see #getProperty(String, Class)
*/
String
getProperty
(
String
key
,
String
defaultValue
);
/**
* Return the property value associated with the given key, or {@code null} if
* the key cannot be resolved.
*
* @param key the property name to resolve
* @param targetType the expected type of the property value
* @see #getRequiredProperty(String, Class)
*/
<
T
>
T
getProperty
(
String
key
,
Class
<
T
>
targetType
);
/**
* Return the property value associated with the given key, or
* {@code defaultValue} if the key cannot be resolved.
*
* @param key the property name to resolve
* @param targetType the expected type of the property value
* @param defaultValue the default value to return if no value is found
* @see #getRequiredProperty(String, Class)
*/
<
T
>
T
getProperty
(
String
key
,
Class
<
T
>
targetType
,
T
defaultValue
);
/**
* Return the property value associated with the given key (never {@code null}).
*
* @throws IllegalStateException if the key cannot be resolved
* @see #getRequiredProperty(String, Class)
*/
String
getRequiredProperty
(
String
key
)
throws
IllegalStateException
;
/**
* Return the property value associated with the given key, converted to the
* given targetType (never {@code null}).
*
* @throws IllegalStateException if the given key cannot be resolved
*/
<
T
>
T
getRequiredProperty
(
String
key
,
Class
<
T
>
targetType
)
throws
IllegalStateException
;
/**
* Resolve ${...} placeholders in the given text, replacing them with
* corresponding property values as resolved by {@link #getProperty}.
* Unresolvable placeholders with no default value are ignored and passed
* through unchanged.
*
* @param text the String to resolve
* @return the resolved String (never {@code null})
* @throws IllegalArgumentException if given text is {@code null}
* @see #resolveRequiredPlaceholders
* @see org.springframework.util.SystemPropertyUtils#resolvePlaceholders(String)
*/
String
resolvePlaceholders
(
String
text
);
/**
* Resolve ${...} placeholders in the given text, replacing them with
* corresponding property values as resolved by {@link #getProperty}.
* Unresolvable placeholders with no default value will cause an
* IllegalArgumentException to be thrown.
*
* @return the resolved String (never {@code null})
* @throws IllegalArgumentException if given text is {@code null} or if any
* placeholders are unresolvable
* @see org.springframework.util.SystemPropertyUtils#resolvePlaceholders(String,
* boolean)
*/
String
resolveRequiredPlaceholders
(
String
text
)
throws
IllegalArgumentException
;
}
Prev
1
…
19
20
21
22
23
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment