Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
T
test_block_process
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
community
test_block_process
Commits
6d91bc4d
Commit
6d91bc4d
authored
May 11, 2018
by
Aigul Baimussayeva
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
block process
parents
Changes
15
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
1241 additions
and
0 deletions
+1241
-0
build.gradle
build.gradle
+40
-0
settings.gradle
settings.gradle
+2
-0
src/main/java/kz/arta/test/blocking/BlockProcessListener.java
...main/java/kz/arta/test/blocking/BlockProcessListener.java
+111
-0
src/main/java/kz/arta/test/blocking/data/ASFData.java
src/main/java/kz/arta/test/blocking/data/ASFData.java
+0
-0
src/main/java/kz/arta/test/blocking/data/ASFDataUtils.java
src/main/java/kz/arta/test/blocking/data/ASFDataUtils.java
+193
-0
src/main/java/kz/arta/test/blocking/data/ASFDataWrapper.java
src/main/java/kz/arta/test/blocking/data/ASFDataWrapper.java
+214
-0
src/main/java/kz/arta/test/blocking/data/ASFDataWrapperExt.java
...in/java/kz/arta/test/blocking/data/ASFDataWrapperExt.java
+21
-0
src/main/java/kz/arta/test/blocking/data/WidgetType.java
src/main/java/kz/arta/test/blocking/data/WidgetType.java
+155
-0
src/main/java/kz/arta/test/blocking/utils/ApiOperation.java
src/main/java/kz/arta/test/blocking/utils/ApiOperation.java
+18
-0
src/main/java/kz/arta/test/blocking/utils/AsfDataApi.java
src/main/java/kz/arta/test/blocking/utils/AsfDataApi.java
+74
-0
src/main/java/kz/arta/test/blocking/utils/DictApi.java
src/main/java/kz/arta/test/blocking/utils/DictApi.java
+26
-0
src/main/java/kz/arta/test/blocking/utils/FormApiOperation.java
...in/java/kz/arta/test/blocking/utils/FormApiOperation.java
+44
-0
src/main/java/kz/arta/test/blocking/utils/HttpBasicOperation.java
.../java/kz/arta/test/blocking/utils/HttpBasicOperation.java
+177
-0
src/main/java/kz/arta/test/blocking/utils/PropsUtil.java
src/main/java/kz/arta/test/blocking/utils/PropsUtil.java
+141
-0
src/main/java/kz/arta/test/blocking/utils/UnblockOperation.java
...in/java/kz/arta/test/blocking/utils/UnblockOperation.java
+25
-0
No files found.
build.gradle
0 → 100644
View file @
6d91bc4d
group
'test_block_process'
apply
plugin:
'java'
apply
plugin:
'war'
sourceCompatibility
=
1.7
repositories
{
mavenCentral
()
mavenLocal
()
maven
{
url
'http://artifactory.lan.arta.kz/artifactory/synergy-libs/'
}
}
sourceSets
{
main
{
resources
.
srcDirs
=
[
"src/main/java"
,
"src/main/resources"
]
}
}
dependencies
{
testCompile
group:
'junit'
,
name:
'junit'
,
version:
'4.11'
providedCompile
"javax:javaee-api:6.0"
providedCompile
group:
'org.slf4j'
,
name:
'slf4j-api'
,
version:
'1.6.1'
,
transitive:
false
providedCompile
"log4j:log4j:1.2.16"
compile
group:
'commons-lang'
,
name:
'commons-lang'
,
version:
'2.2'
compile
"org.codehaus.jackson:jackson-mapper-asl:1.9.2"
compile
"org.codehaus.jackson:jackson-core-asl:1.9.2"
providedCompile
'org.apache.httpcomponents:httpclient:4.5'
compile
"commons-io:commons-io:2.1"
compile
group:
'com.google.guava'
,
name:
'guava-gwt'
,
version:
'18.0'
}
war
{
archiveName
=
'blocking.war'
}
settings.gradle
0 → 100644
View file @
6d91bc4d
rootProject
.
name
=
'test_block_process'
src/main/java/kz/arta/test/blocking/BlockProcessListener.java
0 → 100644
View file @
6d91bc4d
package
kz.arta.test.blocking
;
import
kz.arta.test.blocking.data.ASFData
;
import
kz.arta.test.blocking.data.ASFDataWrapperExt
;
import
kz.arta.test.blocking.utils.AsfDataApi
;
import
kz.arta.test.blocking.utils.DictApi
;
import
kz.arta.test.blocking.utils.PropsUtil
;
import
kz.arta.test.blocking.utils.UnblockOperation
;
import
org.codehaus.jackson.JsonNode
;
import
org.codehaus.jackson.map.ObjectMapper
;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
import
javax.ejb.ActivationConfigProperty
;
import
javax.ejb.MessageDriven
;
import
javax.jms.Message
;
import
javax.jms.MessageListener
;
import
javax.jms.TextMessage
;
/**
* User: Aigul
* Date: 10.05.18
* Time: 17:08
*/
@MessageDriven
(
name
=
"TestBlockQueue"
,
activationConfig
=
{
@ActivationConfigProperty
(
propertyName
=
"destinationType"
,
propertyValue
=
"javax.jms.Queue"
),
@ActivationConfigProperty
(
propertyName
=
"destination"
,
propertyValue
=
"java:jboss/queues/Synergy/TestBlockQueue"
),
@ActivationConfigProperty
(
propertyName
=
"reconnectAttempts"
,
propertyValue
=
"32"
),
@ActivationConfigProperty
(
propertyName
=
"reconnectInterval"
,
propertyValue
=
"4000"
),
@ActivationConfigProperty
(
propertyName
=
"acknowledgeMode"
,
propertyValue
=
"Auto-acknowledge"
)
})
public
class
BlockProcessListener
implements
MessageListener
{
private
static
final
Logger
LOGGER
=
LoggerFactory
.
getLogger
(
BlockProcessListener
.
class
);
private
static
final
ObjectMapper
REGULAR_MAPPER
=
new
ObjectMapper
();
private
static
final
String
EVENT_STATUS
=
"event.blocking.test.blocking"
;
@Override
public
void
onMessage
(
Message
message
)
{
String
executionID
=
null
;
boolean
success
=
false
;
String
resultMessage
=
null
;
try
{
String
event
=
message
.
getStringProperty
(
"api_event"
);
LOGGER
.
error
(
event
);
JsonNode
node
=
REGULAR_MAPPER
.
readTree
(((
TextMessage
)
message
).
getText
());
String
dataUUID
=
node
.
get
(
"dataUUID"
).
asText
();
String
documentID
=
node
.
get
(
"documentID"
).
asText
();
executionID
=
node
.
get
(
"executionID"
).
asText
();
success
=
true
;
if
(
event
.
startsWith
(
EVENT_STATUS
))
{
AsfDataApi
asfDataApi
=
new
AsfDataApi
(
PropsUtil
.
getAddress
(),
PropsUtil
.
getAuthEncoded
());
/*получаем данные по форме*/
ASFDataWrapperExt
data
=
asfDataApi
.
getAsfData
(
dataUUID
);
/*получаем данные выпадающего списка*/
ASFData
.
Data
listbox
=
data
.
getData
(
"listbox-test"
);
String
statusKey
=
event
.
substring
(
EVENT_STATUS
.
length
()
+
1
);
listbox
.
setKey
(
statusKey
);
String
statusValue
=
getDictStatusValue
(
statusKey
);
if
(
statusValue
!=
null
)
{
listbox
.
setValue
(
statusValue
);
}
/*сохранить сами данные по форме*/
asfDataApi
.
saveData
(
REGULAR_MAPPER
.
writeValueAsString
(
data
.
getData
()),
data
.
getForm
(),
data
.
getUuid
(),
null
);
}
}
catch
(
Exception
exc
)
{
LOGGER
.
error
(
"Error processing text message"
,
exc
);
}
finally
{
if
(
executionID
!=
null
)
{
if
(
resultMessage
==
null
)
{
resultMessage
=
success
?
"OK"
:
"Error"
;
}
try
{
UnblockOperation
unblockOperation
=
new
UnblockOperation
(
PropsUtil
.
getAddress
(),
PropsUtil
.
getAuthEncoded
());
unblockOperation
.
unblock
(
executionID
,
resultMessage
,
success
?
"got_agree"
:
"got_refuse"
);
}
catch
(
Exception
e
)
{
LOGGER
.
error
(
e
.
getMessage
(),
e
);
}
}
}
}
/**
* Получить название статуса
* @param statusKey ключ статуса
* @return
* @throws Exception
*/
private
String
getDictStatusValue
(
String
statusKey
)
throws
Exception
{
DictApi
dictApi
=
new
DictApi
(
PropsUtil
.
getAddress
(),
PropsUtil
.
getAuthEncoded
());
JsonNode
values
=
REGULAR_MAPPER
.
readTree
(
dictApi
.
getDict
(
"test_statuses"
,
false
,
"ru"
)).
get
(
"items"
);
for
(
JsonNode
node
:
values
)
{
JsonNode
key
=
node
.
get
(
"key"
);
JsonNode
value
=
node
.
get
(
"value"
);
if
(
statusKey
!=
null
&&
statusKey
.
equalsIgnoreCase
(
key
.
get
(
"value"
).
getTextValue
()))
{
return
value
.
get
(
"translation"
).
getTextValue
();
}
}
return
null
;
}
}
src/main/java/kz/arta/test/blocking/data/ASFData.java
0 → 100644
View file @
6d91bc4d
This diff is collapsed.
Click to expand it.
src/main/java/kz/arta/test/blocking/data/ASFDataUtils.java
0 → 100644
View file @
6d91bc4d
package
kz.arta.test.blocking.data
;
import
java.util.*
;
import
java.util.regex.Matcher
;
import
java.util.regex.Pattern
;
/**
* User: vsl
* Date: 3/18/15
* Time: 9:48 AM
*
* Утилиты для работы с данными форм
*/
public
class
ASFDataUtils
{
private
static
final
Pattern
BLOCK_ID_PATTERN
=
Pattern
.
compile
(
"(.*)-b([0-9]{1,})$"
);
/**
* Есть ли табличный индекс в id
*/
public
static
boolean
hasBIndex
(
String
id
)
{
return
BLOCK_ID_PATTERN
.
matcher
(
id
).
matches
();
}
/**
* Возвращает табличный индекс из id, если его нет - null
*/
public
static
String
getBindex
(
String
id
)
{
Matcher
matcher
=
BLOCK_ID_PATTERN
.
matcher
(
id
);
if
(!
matcher
.
matches
())
{
return
null
;
}
else
{
return
matcher
.
group
(
2
);
}
}
public
static
String
generateTableBlockIndex
(
String
realId
,
int
blockIndex
){
return
realId
+
"-b"
+
blockIndex
;
}
/**
* Возвращает часть id без табличного индекса если он есть,
* если нет - просто возвращает id
*/
public
static
String
getBlockComponentId
(
String
id
)
{
Matcher
matcher
=
BLOCK_ID_PATTERN
.
matcher
(
id
);
if
(!
matcher
.
matches
())
{
return
id
;
}
else
{
return
matcher
.
group
(
1
);
}
}
/**
* Возвращает табличный индекс из id, если его нет -1
*/
public
static
int
getIntBindex
(
String
id
)
{
String
bIndex
=
getBindex
(
id
);
if
(
bIndex
==
null
||
bIndex
.
isEmpty
())
{
return
-
1
;
}
try
{
return
Integer
.
parseInt
(
bIndex
);
}
catch
(
NumberFormatException
e
)
{
return
-
1
;
}
}
public
static
void
mergeAsfData
(
List
<
ASFData
.
Data
>
dest
,
List
<
ASFData
.
Data
>
additional
)
throws
IllegalArgumentException
{
if
(
additional
==
null
)
{
return
;
}
ASFData
data
=
new
ASFData
();
data
.
setData
(
dest
);
for
(
ASFData
.
Data
toAdd
:
additional
)
{
data
.
addData
(
toAdd
);
}
}
public
static
Set
<
Integer
>
getBlockNumbers
(
List
<
ASFData
.
Data
>
tableData
){
Set
<
Integer
>
list
=
new
HashSet
<>();
for
(
ASFData
.
Data
data
:
tableData
)
{
int
blockNumber
=
getIntBindex
(
data
.
getId
());
if
(
blockNumber
>
0
)
{
list
.
add
(
blockNumber
);
}
}
return
list
;
}
public
static
void
addAppendableTableData
(
List
<
ASFData
.
Data
>
dest
,
String
tableId
,
List
<
ASFData
.
Data
>
additionalTableData
){
ASFData
.
Data
tableData
=
null
;
for
(
ASFData
.
Data
d
:
dest
)
{
if
(
tableId
.
equals
(
d
.
getId
()))
{
tableData
=
d
;
break
;
}
}
if
(
tableData
==
null
)
{
tableData
=
new
ASFData
.
Data
();
tableData
.
setId
(
tableId
);
tableData
.
setType
(
WidgetType
.
appendable_table
.
name
());
dest
.
add
(
tableData
);
}
int
lastIndex
=
tableData
.
getLastBlockNumber
();
for
(
ASFData
.
Data
data
:
tableData
.
getDataCheckNull
())
{
lastIndex
=
Math
.
max
(
getIntBindex
(
data
.
getId
()),
lastIndex
);
}
Map
<
Integer
,
Integer
>
tableNewIndices
=
new
HashMap
<>();
// map в который мы запоминаем каким блокам какие номера присвоены
// генерируем новые номера блоков
for
(
ASFData
.
Data
data
:
additionalTableData
)
{
int
blockIndex
=
getIntBindex
(
data
.
getId
());
if
(!
tableNewIndices
.
containsKey
(
blockIndex
))
{
tableNewIndices
.
put
(
blockIndex
,
++
lastIndex
);
}
}
for
(
ASFData
.
Data
data
:
additionalTableData
)
{
int
currentIndex
=
getIntBindex
(
data
.
getId
());
int
newIndex
=
tableNewIndices
.
get
(
currentIndex
);
String
cmpId
=
getBlockComponentId
(
data
.
getId
());
data
.
setId
(
generateTableBlockIndex
(
cmpId
,
newIndex
));
tableData
.
addData
(
data
);
}
}
/**
* этот и следующие 5 методов используются в сторонних проектах. не удалять!.
*/
public
static
ASFData
.
Data
addData
(
String
id
,
WidgetType
type
,
ASFData
.
Data
parent
,
String
key
,
String
value
)
{
return
addData
(
id
,
type
,
parent
.
getData
(),
key
,
value
);
}
public
static
ASFData
.
Data
addData
(
String
id
,
WidgetType
type
,
List
<
ASFData
.
Data
>
datas
,
String
key
,
String
value
)
{
ASFData
.
Data
data
=
new
ASFData
.
Data
();
data
.
setId
(
id
);
data
.
setType
(
type
.
name
());
data
.
setValue
(
value
);
data
.
setKey
(
key
);
datas
.
add
(
data
);
return
data
;
}
public
static
ASFData
.
Data
addData
(
String
id
,
WidgetType
type
,
ASFDataWrapper
parent
,
String
key
,
String
value
)
{
return
addData
(
id
,
type
,
parent
.
getData
(),
key
,
value
);
}
public
static
ASFData
.
Data
getCmpData
(
ASFDataWrapper
asfData
,
WidgetType
type
,
String
cmpID
)
{
ASFData
.
Data
data
=
asfData
.
getData
(
cmpID
);
if
(
data
==
null
)
{
data
=
addData
(
cmpID
,
type
,
asfData
.
getData
(),
null
,
null
);
if
(
type
==
WidgetType
.
appendable_table
)
{
data
.
setData
(
new
ArrayList
<
ASFData
.
Data
>());
}
}
return
data
;
}
public
static
void
setData
(
String
id
,
WidgetType
type
,
ASFData
.
Data
reserveTable
,
String
key
,
String
value
)
{
ASFData
.
Data
data
=
reserveTable
.
getData
(
id
);
if
(
data
==
null
)
{
data
=
addData
(
id
,
type
,
reserveTable
,
null
,
null
);
}
data
.
setValue
(
value
);
data
.
setKey
(
key
);
}
public
static
void
setData
(
String
id
,
WidgetType
type
,
ASFDataWrapper
reserveTable
,
String
key
,
String
value
)
{
ASFData
.
Data
data
=
reserveTable
.
getData
(
id
);
if
(
data
==
null
)
{
data
=
addData
(
id
,
type
,
reserveTable
,
null
,
null
);
}
data
.
setValue
(
value
);
data
.
setKey
(
key
);
}
}
src/main/java/kz/arta/test/blocking/data/ASFDataWrapper.java
0 → 100644
View file @
6d91bc4d
package
kz.arta.test.blocking.data
;
import
org.codehaus.jackson.annotate.JsonIgnoreProperties
;
import
java.util.ArrayList
;
import
java.util.HashMap
;
import
java.util.List
;
import
java.util.Map
;
/**
* Created by val
* Date: 15.05.2013
* Time: 12:40
*/
@JsonIgnoreProperties
(
ignoreUnknown
=
true
)
public
class
ASFDataWrapper
{
private
String
nodeUUID
;
private
String
uuid
;
private
String
form
;
private
Integer
formVersion
;
private
String
modified
;
private
List
<
ASFData
.
Data
>
data
;
public
String
getUuid
()
{
return
uuid
;
}
public
void
setUuid
(
String
uuid
)
{
this
.
uuid
=
uuid
;
}
public
String
getForm
()
{
return
form
;
}
public
void
setForm
(
String
form
)
{
this
.
form
=
form
;
}
public
Integer
getFormVersion
()
{
return
formVersion
;
}
public
void
setFormVersion
(
Integer
formVersion
)
{
this
.
formVersion
=
formVersion
;
}
public
String
getModified
()
{
return
modified
;
}
public
void
setModified
(
String
modified
)
{
this
.
modified
=
modified
;
}
public
List
<
ASFData
.
Data
>
getData
()
{
return
data
;
}
/**
* Возвращает данные компонента по его идентификатору. Не осуществляет поиск внутри динамической таблицы.
* @param cmpID идентификатор компонента формы
* @return данные компонента
*/
public
ASFData
.
Data
getData
(
String
cmpID
)
{
if
(
data
!=
null
)
{
for
(
ASFData
.
Data
d
:
data
)
{
if
(
d
.
getId
().
equalsIgnoreCase
(
cmpID
))
return
d
;
}
}
return
null
;
}
/**
* Возвращает данные компонента по его идентификатору. Осуществляет поиск внутри динамической таблицы.
* @param cmpID идентификатор компонента формы
* @param searchInsideTables искать внутри динамической таблицы
* @return данные компонента
*/
public
ASFData
.
Data
getData
(
String
cmpID
,
boolean
searchInsideTables
)
{
if
(!
searchInsideTables
)
return
getData
(
cmpID
);
if
(
data
!=
null
)
{
for
(
ASFData
.
Data
d
:
data
)
{
if
(
d
.
getData
()
!=
null
&&
d
.
getData
().
size
()
>
0
)
{
for
(
ASFData
.
Data
innerData
:
d
.
getData
())
if
(
innerData
.
getId
().
equalsIgnoreCase
(
cmpID
))
return
innerData
;
}
else
if
(
d
.
getId
().
equalsIgnoreCase
(
cmpID
))
return
d
;
}
}
return
null
;
}
public
List
<
ASFData
.
Data
>
getAllDataById
(
String
cmpID
,
boolean
includeDynamic
)
{
List
<
ASFData
.
Data
>
result
=
new
ArrayList
<>();
for
(
ASFData
.
Data
data
:
this
.
data
)
{
if
(
data
==
null
)
{
continue
;
}
if
(
cmpID
.
equals
(
data
.
getId
()))
{
result
.
add
(
data
);
}
if
(
includeDynamic
&&
data
.
getData
()
!=
null
)
{
for
(
ASFData
.
Data
dynData
:
data
.
getData
())
{
if
(
cmpID
.
equals
(
ASFDataUtils
.
getBlockComponentId
(
dynData
.
getId
())))
{
result
.
add
(
dynData
);
}
}
}
}
return
result
;
}
public
void
setData
(
List
<
ASFData
.
Data
>
data
)
{
this
.
data
=
data
;
}
public
String
getNodeUUID
()
{
return
nodeUUID
;
}
public
void
setNodeUUID
(
String
nodeUUID
)
{
this
.
nodeUUID
=
nodeUUID
;
}
@Override
public
boolean
equals
(
Object
obj
)
{
if
(!
(
obj
instanceof
ASFDataWrapper
))
return
false
;
ASFDataWrapper
data
=
(
ASFDataWrapper
)
obj
;
for
(
ASFData
.
Data
d
:
data
.
getData
()){
String
id
=
d
.
getId
();
ASFData
.
Data
d1
=
getData
(
id
);
boolean
equals
=
equalsData
(
d
,
d1
);
if
(!
equals
)
return
false
;
}
for
(
ASFData
.
Data
d
:
getData
()){
String
id
=
d
.
getId
();
ASFData
.
Data
d1
=
data
.
getData
(
id
);
boolean
equals
=
equalsData
(
d
,
d1
);
if
(!
equals
)
return
false
;
}
return
true
;
}
private
boolean
equalsData
(
ASFData
.
Data
d
,
ASFData
.
Data
d1
){
if
(
d1
==
null
||
!
equals
(
d1
.
getValue
(),
d
.
getValue
())
||
!
equals
(
d1
.
getKey
(),
d
.
getKey
())
||
!
equals
(
d1
.
getKeys
(),
d
.
getKeys
())
||
!
equals
(
d1
.
getValues
(),
d
.
getValues
())
)
{
return
false
;
}
if
(
d
.
getData
()!=
null
&&
d1
.
getData
()!=
null
)
{
for
(
ASFData
.
Data
childData
:
d
.
getData
())
{
ASFData
.
Data
childData1
=
d1
.
getData
(
childData
.
getId
());
boolean
equals
=
equalsData
(
childData
,
childData1
);
if
(!
equals
)
return
false
;
}
}
return
true
;
}
/**
* заменяет данные с одинаковыми id на данные из mixin
*/
public
void
mixin
(
List
<
ASFData
.
Data
>
mixin
)
{
if
(
mixin
==
null
)
return
;
Map
<
String
,
ASFData
.
Data
>
idsMap
=
new
HashMap
<>();
for
(
ASFData
.
Data
data
:
mixin
)
{
String
id
=
data
.
getId
();
if
(
id
==
null
||
id
.
isEmpty
())
continue
;
idsMap
.
put
(
id
,
data
);
}
List
<
ASFData
.
Data
>
mixed
=
new
ArrayList
<>();
for
(
ASFData
.
Data
data
:
this
.
data
)
{
if
(
idsMap
.
containsKey
(
data
.
getId
()))
mixed
.
add
(
idsMap
.
get
(
data
.
getId
()));
else
mixed
.
add
(
data
);
}
this
.
data
=
mixed
;
}
public
static
boolean
equals
(
Object
object1
,
Object
object2
)
{
return
object1
==
object2
?
true
:
(
object1
!=
null
&&
object2
!=
null
?
object1
.
equals
(
object2
)
:
false
);
}
@Override
public
int
hashCode
()
{
return
super
.
hashCode
();
}
}
src/main/java/kz/arta/test/blocking/data/ASFDataWrapperExt.java
0 → 100644
View file @
6d91bc4d
package
kz.arta.test.blocking.data
;
import
org.codehaus.jackson.annotate.JsonIgnoreProperties
;
/**
* Created by exile
* Date: 27.09.16
* Time: 11:00
*/
@JsonIgnoreProperties
(
ignoreUnknown
=
true
)
public
class
ASFDataWrapperExt
extends
ASFDataWrapper
{
private
int
version
;
public
int
getVersion
()
{
return
version
;
}
public
void
setVersion
(
int
version
)
{
this
.
version
=
version
;
}
}
src/main/java/kz/arta/test/blocking/data/WidgetType.java
0 → 100644
View file @
6d91bc4d
package
kz.arta.test.blocking.data
;
/**
* Created by IntelliJ IDEA.
* User: exile
* Date: 02.04.13
* Time: 14:00
* <br/><br/>
* Перечисление типов компонентов формы.
* Используется при получении списка компонентов формы.
*/
public
enum
WidgetType
{
label
(
"FWLabel"
,
false
),
textbox
(
"FWText"
,
true
,
true
),
textarea
(
"FWTextArea"
,
true
,
true
),
listbox
(
"FWListBox"
,
true
,
true
),
/**
* Переключатель вариантов, сохранение ключа-значения реверсивное, в key - хранится значение, в value - ключ
*/
radio
(
"FWRadionList"
,
true
,
true
),
/**
* Выбор вариантов. Значения хранятся в массиве keys, ключи в массиве values. Key и value пустые
*/
check
(
"FWCheckList"
,
true
,
true
),
date
(
"FWDate"
,
true
,
true
),
table
(
"FWTable"
,
false
),
entity
(
"FWSynergyEntity"
,
true
,
true
),
image
(
"FWStaticImage"
,
false
),
file
(
"FWFileAttachment"
,
false
,
true
),
filelink
(
"FWFileLink"
,
false
,
true
),
userchooser
(
"FWUserChooser"
,
true
,
true
),
counter
(
"FWCounter"
),
signlist
(
"FWSignList"
,
false
),
resolutionlist
(
"FWResolutionsList"
,
false
),
htd
(
"FWHtdEditor"
,
true
,
true
),
doclink
(
"FWSynergyDocLink"
,
false
,
true
),
link
(
"FWLink"
,
true
,
true
),
processlist
(
"FWProcessHistoryList"
,
false
),
repeater
(
"FWRepeatInput"
,
true
,
true
),
reglink
(
"FWSynergyRegLink"
,
true
,
true
),
projectlink
(
"FWSynergyProjLink"
,
true
,
true
),
personlink
(
"FWSynergyPersonLink"
,
true
,
true
),
appendable_table
(
"FWAppendableTable"
),
/**
* пользовательский компонент
*/
custom
(
"FWCustom"
,
true
),
/**
* @deprecated - не используется
*/
@Deprecated
users
(
"FWDUser"
),
departments
(
"FWDepartmentChooser"
,
true
,
true
),
positions
(
"FWPositionChooser"
,
true
,
true
),
docnumber
(
"FWDocNumber"
,
false
)
/* НА САМОМ ДЕЛЕ ЭТО СВОЙСТВА ДОКУМЕНТА*/
,
numericinput
(
"FWNumericInput"
,
true
,
true
),
errorlabel
(
"FWErrorLabel"
,
false
),
page
(
"FWDPage"
,
false
),
;
/**
* Говорит является ли компонент полем ввода,
* а также содержит и хранит значение.
*/
private
final
boolean
input
;
/**
* Может ли пользователь непосредственно изменять значение виджета
*/
private
final
boolean
editable
;
/**
* Имя класса которое используется для элемента
*/
private
final
String
className
;
WidgetType
(
String
className
)
{
this
(
className
,
true
,
false
);
}
WidgetType
(
String
className
,
boolean
input
)
{
this
(
className
,
input
,
false
);
}
WidgetType
(
String
className
,
boolean
input
,
boolean
editable
)
{
this
.
input
=
input
;
this
.
className
=
className
;
this
.
editable
=
editable
;
}
public
String
getClassName
()
{
return
className
;
}
public
boolean
isInput
()
{
return
input
;
}
public
boolean
isEditable
()
{
return
editable
;
}
/**
* Является ли строка типом widget-а
*
* @param widgetType - тип widget-a
* @return - является/не является
*/
public
static
boolean
contains
(
String
widgetType
)
{
return
getFromString
(
widgetType
)
!=
null
;
}
/**
* По названию enum возвращает его
*
* @param typeStr название enum
* @return enum
*/
public
static
WidgetType
getFromString
(
String
typeStr
)
{
for
(
WidgetType
type
:
values
())
{
if
(
type
.
name
().
equals
(
typeStr
))
{
return
type
;
}
}
return
null
;
}
/**
* по полю не возможна сортировка и оно не будет быть значащим содержимым, но может быть отображено в таблице
* @return true
*/
public
boolean
notSortingViewField
(){
return
isInput
()
&&
this
!=
WidgetType
.
appendable_table
;
}
/**
* надо ли загружать данные формы, чтобы узнать ее значения
* @return true - да
*/
public
boolean
shouldLoadDataToGetValue
(){
return
this
==
WidgetType
.
doclink
||
this
==
WidgetType
.
filelink
||
this
==
WidgetType
.
table
||
this
==
WidgetType
.
check
;
}
}
src/main/java/kz/arta/test/blocking/utils/ApiOperation.java
0 → 100644
View file @
6d91bc4d
package
kz.arta.test.blocking.utils
;
/**
* User: Aigul
* Date: 3/18/17
* Time: 4:58 PM
*/
public
class
ApiOperation
extends
HttpBasicOperation
{
protected
String
address
;
protected
String
auth
;
public
ApiOperation
(
String
address
,
String
auth
)
{
this
.
address
=
address
;
this
.
auth
=
auth
;
}
}
src/main/java/kz/arta/test/blocking/utils/AsfDataApi.java
0 → 100644
View file @
6d91bc4d
package
kz.arta.test.blocking.utils
;
import
kz.arta.test.blocking.data.ASFDataWrapperExt
;
import
java.io.DataOutputStream
;
import
java.io.IOException
;
import
java.net.HttpURLConnection
;
import
java.net.URL
;
import
java.net.URLEncoder
;
import
java.nio.charset.StandardCharsets
;
/**
* User: Aigul
* Date: 3/18/17
* Time: 4:52 PM
*/
public
class
AsfDataApi
extends
ApiOperation
{
public
AsfDataApi
(
String
address
,
String
auth
)
{
super
(
address
,
auth
);
}
public
ASFDataWrapperExt
getAsfData
(
String
dataUUID
)
throws
IOException
{
return
getAsfData
(
dataUUID
,
0
);
}
public
ASFDataWrapperExt
getAsfData
(
String
dataUUID
,
int
version
)
throws
IOException
{
HttpURLConnection
connection
=
openGetConnection
(
new
URL
(
address
+
"/rest/api/asforms/data/"
+
dataUUID
+
"?version="
+
version
),
auth
);
return
readResult
(
connection
,
ASFDataWrapperExt
.
class
);
}
public
String
saveData
(
String
data
,
String
formId
,
String
dataUUID
,
String
eventProperties
)
throws
IOException
{
HttpURLConnection
connection
=
openPostConnection
(
new
URL
(
address
+
"/rest/api/asforms/data/save"
),
auth
,
null
,
"application/x-www-form-urlencoded;charset=utf-8"
);
DataOutputStream
request
=
new
DataOutputStream
(
connection
.
getOutputStream
());
request
.
writeBytes
(
"formUUID="
+
formId
);
request
.
writeBytes
(
"&"
);
request
.
writeBytes
(
"uuid="
+
dataUUID
);
request
.
writeBytes
(
"&"
);
request
.
writeBytes
(
"data=\"data\":"
);
request
.
write
(
URLEncoder
.
encode
(
data
,
"UTF-8"
).
getBytes
(
"UTF-8"
));
request
.
writeBytes
(
"&"
);
request
.
writeBytes
(
"eventProperties="
);
if
(
eventProperties
!=
null
)
{
request
.
write
(
URLEncoder
.
encode
(
eventProperties
,
"UTF-8"
).
getBytes
(
"UTF-8"
));
}
request
.
flush
();
request
.
close
();
return
readResult
(
connection
);
}
public
String
saveDataWithCode
(
String
data
,
String
formCode
,
String
dataUUID
,
String
eventProperties
)
throws
IOException
{
HttpURLConnection
connection
=
openPostConnection
(
new
URL
(
address
+
"/rest/api/asforms/data/save"
),
auth
,
null
,
"application/x-www-form-urlencoded;charset=utf-8"
);
DataOutputStream
request
=
new
DataOutputStream
(
connection
.
getOutputStream
());
request
.
writeBytes
(
"formCode="
+
URLEncoder
.
encode
(
formCode
,
StandardCharsets
.
UTF_8
.
name
()));
request
.
writeBytes
(
"&"
);
request
.
writeBytes
(
"uuid="
+
dataUUID
);
request
.
writeBytes
(
"&"
);
request
.
writeBytes
(
"data=\"data\":"
);
request
.
write
(
URLEncoder
.
encode
(
data
,
StandardCharsets
.
UTF_8
.
name
()).
getBytes
(
StandardCharsets
.
UTF_8
.
name
()));
request
.
writeBytes
(
"&"
);
request
.
writeBytes
(
"eventProperties="
);
if
(
eventProperties
!=
null
)
{
request
.
write
(
URLEncoder
.
encode
(
eventProperties
,
StandardCharsets
.
UTF_8
.
name
()).
getBytes
(
StandardCharsets
.
UTF_8
.
name
()));
}
request
.
flush
();
request
.
close
();
return
readResult
(
connection
);
}
}
src/main/java/kz/arta/test/blocking/utils/DictApi.java
0 → 100644
View file @
6d91bc4d
package
kz.arta.test.blocking.utils
;
import
java.io.IOException
;
import
java.net.HttpURLConnection
;
import
java.net.URL
;
import
java.net.URLEncoder
;
import
java.nio.charset.StandardCharsets
;
/**
* User: Aigul
* Date: 3/18/17
* Time: 4:24 PM
*/
public
class
DictApi
extends
ApiOperation
{
public
DictApi
(
String
address
,
String
auth
)
{
super
(
address
,
auth
);
}
public
String
getDict
(
String
dictCode
,
boolean
loadColumns
,
String
locale
)
throws
IOException
{
HttpURLConnection
connection
=
openGetConnection
(
new
URL
(
address
+
"/rest/api/dictionaries/"
+
URLEncoder
.
encode
(
dictCode
,
StandardCharsets
.
UTF_8
.
name
())+
"?getColumns="
+
loadColumns
+
"&locale="
+
locale
),
auth
);
return
readResult
(
connection
);
}
}
src/main/java/kz/arta/test/blocking/utils/FormApiOperation.java
0 → 100644
View file @
6d91bc4d
package
kz.arta.test.blocking.utils
;
import
java.io.DataOutputStream
;
import
java.io.IOException
;
import
java.net.HttpURLConnection
;
import
java.net.URL
;
import
java.net.URLEncoder
;
/**
* User: Aigul
* Date: 4/15/17
* Time: 7:24 PM
*/
public
class
FormApiOperation
extends
ApiOperation
{
public
FormApiOperation
(
String
address
,
String
auth
)
{
super
(
address
,
auth
);
}
public
String
getAsfDataUUID
(
String
documentID
)
throws
Exception
{
HttpURLConnection
connection
=
openGetConnection
(
new
URL
(
address
+
"/rest/api/formPlayer/getAsfDataUUID?documentID="
+
documentID
),
auth
,
"text/plain; charset=utf-8"
);
return
this
.
readResult
(
connection
);
}
public
String
saveDataMultiPart
(
String
data
,
String
formId
,
String
dataUUID
,
String
eventProperties
)
throws
IOException
{
HttpURLConnection
connection
=
this
.
openPostConnection
(
new
URL
(
this
.
address
+
"/rest/api/asforms/form/multipartdata"
),
this
.
auth
,
null
,
"application/x-www-form-urlencoded;charset=utf-8"
);
DataOutputStream
request
=
new
DataOutputStream
(
connection
.
getOutputStream
());
request
.
writeBytes
(
"form="
+
formId
);
request
.
writeBytes
(
"&"
);
request
.
writeBytes
(
"uuid="
+
dataUUID
);
request
.
writeBytes
(
"&"
);
request
.
writeBytes
(
"data="
);
request
.
write
(
URLEncoder
.
encode
(
data
,
"UTF-8"
).
getBytes
(
"UTF-8"
));
request
.
writeBytes
(
"&"
);
request
.
writeBytes
(
"eventProperties="
);
if
(
eventProperties
!=
null
)
{
request
.
write
(
URLEncoder
.
encode
(
eventProperties
,
"UTF-8"
).
getBytes
(
"UTF-8"
));
}
request
.
flush
();
request
.
close
();
return
this
.
readResult
(
connection
);
}
}
src/main/java/kz/arta/test/blocking/utils/HttpBasicOperation.java
0 → 100644
View file @
6d91bc4d
package
kz.arta.test.blocking.utils
;
import
com.google.common.io.ByteStreams
;
import
com.google.common.primitives.Ints
;
import
org.apache.commons.io.IOUtils
;
import
org.codehaus.jackson.JsonNode
;
import
org.codehaus.jackson.map.ObjectMapper
;
import
java.io.DataOutputStream
;
import
java.io.IOException
;
import
java.io.InputStream
;
import
java.io.OutputStream
;
import
java.net.HttpURLConnection
;
import
java.net.URL
;
import
java.net.URLEncoder
;
import
java.nio.charset.StandardCharsets
;
import
java.util.Map
;
/**
* Created by exile
* Date: 10.01.17
* Time: 12:12
*/
public
class
HttpBasicOperation
{
public
static
ObjectMapper
mapper
=
new
ObjectMapper
();
public
HttpURLConnection
openPostConnection
(
URL
url
)
throws
IOException
{
return
openPostConnection
(
url
,
null
);
}
public
HttpURLConnection
openPostConnection
(
URL
url
,
String
auth
)
throws
IOException
{
return
openConnection
(
url
,
"POST"
,
auth
,
null
,
null
);
}
public
HttpURLConnection
openPostConnection
(
URL
url
,
String
auth
,
String
accept
)
throws
IOException
{
return
openConnection
(
url
,
"POST"
,
auth
,
accept
,
null
);
}
public
HttpURLConnection
openPostConnection
(
URL
url
,
String
auth
,
String
accept
,
String
contentType
)
throws
IOException
{
return
openConnection
(
url
,
"POST"
,
auth
,
accept
,
contentType
);
}
public
HttpURLConnection
openDeleteConnection
(
URL
url
)
throws
IOException
{
return
openConnection
(
url
,
"DELETE"
,
null
,
null
,
null
);
}
public
HttpURLConnection
openGetConnection
(
URL
url
)
throws
IOException
{
return
openGetConnection
(
url
,
null
);
}
public
HttpURLConnection
openGetConnection
(
URL
url
,
String
auth
)
throws
IOException
{
return
openGetConnection
(
url
,
auth
,
null
);
}
public
HttpURLConnection
openGetConnection
(
URL
url
,
String
auth
,
String
accept
)
throws
IOException
{
return
openConnection
(
url
,
"GET"
,
auth
,
accept
,
null
);
}
public
HttpURLConnection
openHeadConnection
(
URL
url
)
throws
IOException
{
return
openConnection
(
url
,
"HEAD"
,
null
,
null
,
null
);
}
public
HttpURLConnection
openPutConnection
(
URL
url
)
throws
IOException
{
return
openConnection
(
url
,
"PUT"
,
null
,
null
,
null
);
}
private
HttpURLConnection
openConnection
(
URL
url
,
String
method
,
String
auth
,
String
accept
,
String
contentType
)
throws
IOException
{
HttpURLConnection
conn
=
(
HttpURLConnection
)
url
.
openConnection
();
conn
.
setRequestMethod
(
method
);
if
(
method
.
equals
(
"POST"
))
{
conn
.
setDoOutput
(
true
);
}
addHeaders
(
conn
,
auth
,
accept
,
contentType
);
return
conn
;
}
private
void
addHeaders
(
HttpURLConnection
conn
,
String
auth
,
String
accept
,
String
contentType
){
if
(
contentType
==
null
)
{
conn
.
setRequestProperty
(
"Content-Type"
,
"application/json; charset=utf-8"
);
}
else
{
conn
.
setRequestProperty
(
"Content-Type"
,
contentType
);
}
if
(
accept
==
null
)
{
conn
.
setRequestProperty
(
"Accept"
,
"application/json; charset=utf-8"
);
}
else
{
conn
.
setRequestProperty
(
"Accept"
,
accept
);
}
if
(
auth
!=
null
)
{
conn
.
setRequestProperty
(
"Authorization"
,
auth
);
}
}
public
void
writeValue
(
HttpURLConnection
connection
,
Object
value
)
throws
IOException
{
connection
.
setDoOutput
(
true
);
try
(
OutputStream
stream
=
connection
.
getOutputStream
())
{
mapper
.
writeValue
(
stream
,
value
);
}
catch
(
IOException
e
)
{
connection
.
disconnect
();
throw
e
;
}
}
public
void
writeParams
(
HttpURLConnection
connection
,
Map
<
String
,
String
>
params
)
throws
IOException
{
connection
.
setDoOutput
(
true
);
DataOutputStream
request
=
new
DataOutputStream
(
connection
.
getOutputStream
());
for
(
String
key
:
params
.
keySet
()){
request
.
writeBytes
(
key
+
"="
+
URLEncoder
.
encode
(
params
.
get
(
key
),
StandardCharsets
.
UTF_8
.
name
()));
request
.
writeBytes
(
"&"
);
}
}
public
String
readResult
(
HttpURLConnection
conn
)
throws
IOException
{
try
{
InputStream
in
=
conn
.
getInputStream
();
return
IOUtils
.
toString
(
in
,
StandardCharsets
.
UTF_8
.
name
());
}
catch
(
IOException
e
)
{
throw
new
IOException
(
readError
(
conn
),
e
);
}
finally
{
conn
.
disconnect
();
}
}
public
JsonNode
readJsonResult
(
HttpURLConnection
conn
)
throws
IOException
{
try
{
return
mapper
.
readTree
(
conn
.
getInputStream
());
}
catch
(
IOException
e
)
{
throw
new
IOException
(
readError
(
conn
),
e
);
}
finally
{
conn
.
disconnect
();
}
}
public
<
T
>
T
readResult
(
HttpURLConnection
conn
,
Class
<
T
>
resultClass
,
T
httpErrorCodeInstance
,
int
...
allowedStatusCodes
)
throws
IOException
{
try
{
return
mapper
.
readValue
(
conn
.
getInputStream
(),
resultClass
);
}
catch
(
IOException
e
)
{
int
code
=
conn
.
getResponseCode
();
if
(
allowedStatusCodes
!=
null
)
{
if
(
Ints
.
contains
(
allowedStatusCodes
,
code
))
{
return
httpErrorCodeInstance
;
}
}
throw
new
IOException
(
readError
(
conn
),
e
);
}
finally
{
conn
.
disconnect
();
}
}
public
<
T
>
T
readResult
(
HttpURLConnection
conn
,
Class
<
T
>
resultClass
)
throws
IOException
{
try
{
InputStream
in
=
conn
.
getInputStream
();
byte
[]
b
=
ByteStreams
.
toByteArray
(
in
);
if
(
b
.
length
==
0
)
{
return
null
;
}
return
mapper
.
readValue
(
b
,
resultClass
);
}
catch
(
IOException
e
)
{
throw
new
IOException
(
readError
(
conn
),
e
);
}
finally
{
conn
.
disconnect
();
}
}
private
String
readError
(
HttpURLConnection
conn
){
try
{
InputStream
in
=
conn
.
getErrorStream
();
byte
[]
b
=
ByteStreams
.
toByteArray
(
in
);
return
new
String
(
b
,
StandardCharsets
.
UTF_8
);
}
catch
(
Exception
e
)
{
return
e
.
getMessage
();
}
}
}
src/main/java/kz/arta/test/blocking/utils/PropsUtil.java
0 → 100644
View file @
6d91bc4d
package
kz.arta.test.blocking.utils
;
import
org.slf4j.Logger
;
import
javax.xml.bind.DatatypeConverter
;
import
java.io.File
;
import
java.io.FileInputStream
;
import
java.io.InputStreamReader
;
import
java.net.MalformedURLException
;
import
java.net.URL
;
import
java.util.Date
;
import
java.util.Properties
;
/**
* User: Aigul
* Date: 3/18/17
* Time: 3:43 PM
*/
public
class
PropsUtil
{
public
static
final
String
PROP_FILE
=
"arta/apps/test_blocking/test.properties"
;
public
static
final
String
SYNERGY_ADDRESS_KEY
=
"synergy.address"
;
public
static
final
String
SYNERGY_LOGIN_KEY
=
"user.login"
;
public
static
final
String
SYNERGY_PASSWORD_KEY
=
"user.password"
;
private
static
final
Logger
LOGGER
=
org
.
slf4j
.
LoggerFactory
.
getLogger
(
PropsUtil
.
class
);
private
static
Properties
properties
;
private
static
long
lastUpdatedTime
=
-
1
l
;
private
static
PropsUtil
baiConfigInstance
=
new
PropsUtil
();
private
PropsUtil
()
{
}
public
static
PropsUtil
getInstance
()
{
return
baiConfigInstance
;
}
/**
* Получение относительного пути конфигурационного файла
*/
private
static
String
getConfFilePath
(
String
relativePath
)
{
return
getCfgURL
()
+
"/"
+
relativePath
;
}
/**
* поднимаем конфиг директорию
*
* @return
*/
public
static
String
getCfgURL
()
{
String
cfgDir
=
System
.
getProperty
(
"jboss.server.config.url"
);
if
(
cfgDir
!=
null
&&
cfgDir
.
startsWith
(
"file://"
))
{
try
{
URL
url
=
new
URL
(
cfgDir
);
return
url
.
getFile
();
}
catch
(
MalformedURLException
e
)
{
LOGGER
.
error
(
e
.
getMessage
(),
e
);
}
}
if
(
cfgDir
==
null
)
{
cfgDir
=
System
.
getProperty
(
"jboss.server.config.dir"
);
if
(
cfgDir
!=
null
)
{
return
cfgDir
;
}
else
{
LOGGER
.
error
(
"NOT FOUND CONFIG DIRECTORY"
);
return
"/home/conf"
;
}
}
return
cfgDir
;
}
public
static
String
getProperty
(
String
key
)
{
return
getProps
(
false
).
getProperty
(
key
);
}
public
static
String
get
(
String
key
)
{
return
getProps
(
false
).
getProperty
(
key
);
}
public
static
String
getAddress
()
{
return
getProperty
(
"synergy.address"
);
}
public
static
String
getPassword
()
{
return
getProperty
(
"user.password"
);
}
public
static
String
getLogin
()
{
return
getProperty
(
"user.login"
);
}
private
static
String
getAuthEncoded
(
String
login
,
String
password
)
{
return
DatatypeConverter
.
printBase64Binary
((
login
+
":"
+
password
).
getBytes
());
}
public
static
String
getAuthEncoded
()
{
String
login
=
getLogin
();
String
password
=
getPassword
();
return
"Basic "
+
DatatypeConverter
.
printBase64Binary
((
login
+
":"
+
password
).
getBytes
());
}
/**
* Настройки
*/
public
static
Properties
getProps
(
boolean
updateCache
)
{
long
diffMinutes
=
(
new
Date
().
getTime
()
-
lastUpdatedTime
)
/
(
60
*
1000
);
if
(!
updateCache
&&
lastUpdatedTime
>
0
&&
diffMinutes
<
15
&&
properties
!=
null
)
{
return
properties
;
}
if
(
properties
!=
null
)
return
properties
;
properties
=
new
Properties
();
try
{
File
file
=
new
File
(
getConfFilePath
(
PROP_FILE
));
if
(
file
.
exists
())
{
LOGGER
.
debug
(
"Reading settings from "
+
file
.
getAbsolutePath
());
properties
.
load
(
new
InputStreamReader
(
new
FileInputStream
(
file
),
"UTF-8"
));
}
else
{
properties
=
null
;
LOGGER
.
debug
(
"File "
+
file
.
getAbsolutePath
()
+
" does not exist!"
);
}
}
catch
(
Exception
exc
)
{
properties
=
null
;
LOGGER
.
error
(
exc
.
getMessage
(),
exc
);
}
return
properties
;
}
}
src/main/java/kz/arta/test/blocking/utils/UnblockOperation.java
0 → 100644
View file @
6d91bc4d
package
kz.arta.test.blocking.utils
;
import
java.net.HttpURLConnection
;
import
java.net.URL
;
import
java.net.URLEncoder
;
/**
* User: Aigul
* Date: 4/15/17
* Time: 4:04 PM
*/
public
class
UnblockOperation
extends
ApiOperation
{
public
UnblockOperation
(
String
address
,
String
auth
)
{
super
(
address
,
auth
);
}
public
void
unblock
(
String
executionId
,
String
message
,
String
signal
)
throws
Exception
{
HttpURLConnection
connection
=
this
.
openGetConnection
(
new
URL
(
this
.
address
+
"/rest/api/processes/signal"
+
"?signal="
+
signal
+
"¶m1=resolution&value1="
+
URLEncoder
.
encode
(
message
,
"UTF-8"
)
+
"&executionID="
+
executionId
),
this
.
auth
);
readResult
(
connection
);
}
}
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