eventor是一个python编程工具,用于编程基于事件的活动序列

eventor的Python项目详细描述


Overview

Eventor provides programmer with interface to create events, steps and associations of these artifacts with to create a flow.

It would be easier to show an example.

简单示例

 1 importeventorasevr 2 importlogging 3 fromacrilibimportLoggerAddHostFilter 4  5 appname=os.path.basename(__file__) 6 logger=logging.getLogger(appname) 7  8 defconstruct_and_run(): 9 10 defprog(progname):11 logger.info("doing what %s is doing"%progname)12 returnprogname13 14 ev=evr.Eventor(name=appname)15 16 ev1s=ev.add_event('run_step1')17 ev2s=ev.add_event('run_step2')18 ev3s=ev.add_event('run_step3')19 20 s1=ev.add_step('s1',func=prog,kwargs={'progname':'prog1'},21 triggers={evr.StepStatus.success:(ev2s,),})22 s2=ev.add_step('s2',func=prog,kwargs={'progname':'prog2'},23 triggers={evr.StepStatus.success:(ev3s,),})24 s3=ev.add_step('s3',func=prog,kwargs={'progname':'prog3'},)25 26 ev.add_assoc(ev1s,s1)27 ev.add_assoc(ev2s,s2)28 ev.add_assoc(ev3s,s3)29 30 ev.trigger_event(ev1s,1)31 ev.run()32 ev.close()33 34 35 if__name__=='__main__':36 construct_and_run()

示例输出

The above example with provide the following log output.

[ 2016-11-30 10:07:48,612 ][ INFO ][ Running step s1[1] ][ main.task_wrapper ]
[ 2016-11-30 10:07:48,612 ][ INFO ][ Step completed s1[1], status: success, result 'prog1' ][ main.task_wrapper ]
[ 2016-11-30 10:07:50,649 ][ INFO ][ Running step s2[1] ][ main.task_wrapper ]
[ 2016-11-30 10:07:50,649 ][ INFO ][ Step completed s2[1], status: success, result 'prog2' ][ main.task_wrapper ]
[ 2016-11-30 10:07:52,688 ][ INFO ][ Running step s3[1] ][ main.task_wrapper ]
[ 2016-11-30 10:07:52,689 ][ INFO ][ Step completed s3[1], status: success, result 'prog3' ][ main.task_wrapper ]
[ 2016-11-30 10:07:53,700 ][ INFO ][ Processing finished with: success ][ main.loop_session_start ]

Note: actual logging includes hostname and and procname, e.g., “[ mbp02 ][ MainProcess ]”. These information was omitted from logging herein.

示例突出显示

Eventor (line 10) defines eventor using default database configuration (see bellow).

add_event (e.g., line 12) adds an event named run_step1 to the respective eventor object.

add_step (e.g., line 16) adds step s1 which when triggered would run predefined function prog with key words parameters progname=’prog1’. Additionally, when step would end, if successful, it would trigger event evs2

add_assoc (e.g., line 22) links event evs1 and step s1.

trigger_event (line 26) marks event evs1; when triggers, event is associated with sequence. This would allow multiple invocation.

ev() (line 27) invoke Eventor process that would looks for triggers and tasks to act upon. It ends when there is nothing to do.

logger and Eventor names (lines 6 and 14), Eventor is using hierarchical logger based on Eventor name argument. As such naming convention needs to be aligned among all the files participating with the run.

Program Run File

One important artifact used in Eventor is program’s runner file. Runner file database (sqlite) will be created at execution, if not directed otherwise, at the location of the run (UNIX’s pwd). This file contains information on tasks and triggers that are used in the run and in recovery.

Eventor Interface

事件类发起程序

Eventor(name='',store='',run_mode=RUM_RESTART,recovery_run=None,run_id='',config={})

参数

name: string id for Eventor object initiated.

store: Eventor mechanism is built to work with SQLAlchemy. If store is provided, Eventor first check if store is a tag within config under EVENTOR.DATABASE (or whatever the environment variables EVENTOR_CONFIG_TAG and EVENTOR_DB_CONFIG_TAG points to) section. If the tag exists, it will pick its configuration as database configuration. If store is empty, Eventor will try to look for default database configuration. Otherwise, store will be considered as a path to file that would store runnable (sqlite) information; If not provided, calling module path and name will be used with ‘.db’ extension instead of ‘.py’.

run_mode: can be either RUN_RESTART (default) or RUN_RECOVER; in restart, new instance or the run will be created. In recovery, if shared_db is set, run_id or the recovered program must be provided.

recovery_run: if RUN_RECOVER is used, recovery_run will indicate specific instance of previously recovery run that would be executed.If not provided, latest run would be used.

run_id: unique ID for the program run (excluding recovery_run). It is mandatory in shared_db mode, and if not provided, will be generated.

config: keyword dictionary of default configurations. Available keywords and their default values:

NameDefault ValueDescription
workdir/tmpplace to create necessary artifacts (not in use)
logdir/tmpplace to create debug and error log files
task_constructmp.Processmethod to use for execution of steps
max_concurrent1maximum concurrent processing, if value <1, no limit will be pose
stop_on_exceptionTrueif an exception occurs in a step, stop all processes. If True, new processes will not start. But running processes will be permitted to finish
sleep_between_loops1seconds to sleep between iteration of checking triggers and tasks
shared_dbFalseif set, db must not be in memory. signals that multiple programs will use the same database tables.
envvar_prefixEVENTOR
set prefix for naming environment variable
defined for each step:
{envvar_prefix}STEP_NAME
{envvar_prefix}STEP_SEQUENCE
{envvar_prefix}STEP_RECOVERY
{envvar_prefix}LOGGER_NAME
ssh_config~/.ssh/config
SSH configuration file to use with SSH remote
Invocation of steps.
ssh_hostSSH host configuration name prime host.
ssh_portSSH port to use for SSH connectivity
LOGGINGdictionary of logging configurations.
DATABASESdictionary of database configurations.

配置文件示例
EVENTOR:
   debug: False
   task_construct: process
   envvar_prefix: EVENTOR_
   max_concurrent: -1
   stop_on_exception: True
   sleep_between_loops: 0.25
   sequence_arg_name: None
   day_to_keep_db: 5
   remote_method: ssh
   pass_logger_to_task: False
   shared_db: False

    DATABASES:

        sqfile1:
            dialect: sqlite
            database: /tmp/runly.db

        pgdb1:
            dialect:  postgresql
            drivername :  psycopg2
            username: pgusername
            password: pgpassword
            host:     ubuntu-guest-02
            port:     5433
            database: pyground
            schema: play

    LOGGING:
        logging_level: 10
        logdir: /var/log/eventor
        level_formats:
            10: ('[ %(asctime)-15s ][ %(host)s ][ %(processName)-11s ][ %(levelname)-7s ]'
                 '[ %(message)s ][ %(module)s.%(funcName)s(%(lineno)d) ]')
            default: ('[ %(asctime)-15s ][ %(host)s ][ %(processName)-11s ]'
                      '[ %(levelname)-7s ][ %(message)s ]')
        consolidate: False
        console: True
        file_prefix: ''
        file_suffix: ''
        file_mode: 'a'
        maxBytes: 0
        backupCount: 0
        encoding: 'utf8'
        delay: False
        when: 'h'
        interval: 1
        utc: False
        atTime: 86400

数据库注释

It is possible to create configuration for sqlite memory with cache=shared. However, SQLAlchemy will not work with it well in threaded and multiprocessing environment. Hence, at this point, Eventor does not support it.

eventoradd_event方法

add_event(name,expr=None)

参数

name: string unique id for event

expr: logical expression ‘sqlalchemy’ style to automatically raise this expression.

syntax:

expr : (expr, expr, ...)
     | or_(expr, expr, ...)
     | event
  • if expression is of the first style, logical and will apply.
  • the second expression will apply logical or.
  • the basic atom in expression is even which is the product of add_event.

返回

Event object to use in other add_event expressions, add_assoc methods, or with add_step triggers.

事件add_step方法

add_step(name,func,args=(),kwargs={},triggers={},acquires=[],releases=None,recovery={},config={})

参数

name: string unique id for step

func: callable object that would be call at time if step execution

args: tuple of values that will be passed to func at calling

kwargs: keywords arguments that will be passed to func at calling

triggers: mapping of step statuses to set of events to be triggered as in the following table:

statusdescription
STEP_READYset when task is ready to run (triggered)
STEP_ACTIVEset when task is running
STEP_SUCCESSset when task is successful
STEP_FAILUREset when task fails
STEP_COMPLETEstands for success or failure of task

获取:启动前要获取的资源池元组列表和资源量。

releases:资源池的元组列表和完成后要释放的资源量。如果没有,则默认为获取。如果设置为空列表,则不会释放任何获取的资源。

recovery:将状态状态映射到恢复中应如何处理步骤:

statusdefaultdescription
STEP_READYStepReplay.rerunif in recovery and previous status is ready, rerun
STEP_ACTIVEStepReplay.rerunif in recovery and previous status is active, rerun
STEP_FAILUREStepReplay.rerunif in recovery and previous status is failure, rerun
STEP_SUCCESSStepReplay.skipif in recovery and previous status is success, skip

config:步骤配置的关键字映射重写。

namedefaultdescription
stop_on_exceptionTruestop flow if step ends with Exception

返回

Step object to use in add_assoc method.

事件add_assoc方法

add_assoc(event,*assocs,delay=0)

参数

event: event objects as provided by add_event.

assocs: list of associations objects. List is composed from either events (as returned by add_event) or steps (as returned by add_step)

delay: seconds to wait, once event is triggered, before engaging its associations

返回

N/A

eventor触发器事件方法

trigger_event(event,sequence=None)

参数

event: event objects as provided by add_event.

sequence: unique association of triggered event. Event can be triggered only once per sequence. All derivative triggers will carry the same sequence.

返回

N/A

eventorrun方法

run(max_loops=-1)

调用run时,将生成信息并执行评估事件和任务启动的循环。 在每个循环中引发事件并执行任务。max_loops参数允许控制 要执行的循环。

在简单的例子中,ev.run()Engage Eventor的run()方法。

参数

max_loops: max_loops: number of loops to run. If positive, limits number of loops.
defaults to negative, which would run loops until there are no events to raise and no task to run.

返回

If there was a failure that was not followed by event triggered, result will be False.

事件close方法

close()

当调用close时,eventor对象将关闭其打开的工件。这类似于多处理池上的close方法

在简单的例子中,ev.close()engage Eventor的close()方法

参数

N/A.

返回

N/A.

Recovery

When running in recovery, unless indicated otherwise, latest run (initial or recovery) would be used.

Note that when running a program with the intent to use its recovery capabilities, in-memory store cannot be use. Instead, physical storage must be used.

Here is an example for recovery program and run.

恢复示例

 1 importeventorasevr 2 importlogging 3 importmath 4 fromacrilibimportLoggerAddHostFilter 5  6 appname=os.path.basename(__file__) 7 logger=logging.getLogger(appname) 8  9 defsquare(x):10 y=x*x11 logger.info("Square of %s is %s"%(x,y))12 returny13 14 15 defsquare_root(x):16 y=math.sqrt(x)17 logger.info("Square root of %s is %s"%(x,y))18 returny19 20 21 defdivide(x,y):22 z=x/y23 logger.info("dividing %s by %s is %s"%(x,y,z))24 returnz25 26 defbuild_flow(run_mode=evr.RUN_RESTART,param=9,run_id=None):27 ev=evr.Eventor(name=appname,run_mode=run_mode,run_id=run_id,28 config={'LOGGING':29 {'logging_level':logging.INFO}},))30 31 ev1s=ev.add_event('run_step1')32 ev1d=ev.add_event('done_step1')33 ev2s=ev.add_event('run_step2')34 ev2d=ev.add_event('done_step2')35 ev3s=ev.add_event('run_step3',expr=(ev1d,ev2d))36 37 s1=ev.add_step('s1',func=square,kwargs={'x':3},38 triggers={evr.STEP_SUCCESS:(ev1d,ev2s,)},)39 s2=ev.add_step('s2',square_root,kwargs={'x':param},40 triggers={evr.STEP_SUCCESS:(ev2d,),},41 recovery={evr.STEP_FAILURE:evr.STEP_RERUN,42 evr.STEP_SUCCESS:evr.STEP_SKIP})43 s3=ev.add_step('s3',divide,kwargs={'x':9,'y':3},)44 45 ev.add_assoc(ev1s,s1)46 ev.add_assoc(ev2s,s2)47 ev.add_assoc(ev3s,s3)48 ev.trigger_event(ev1s,3)49 returnev50 51 52 defconstruct_and_run():53 # start regularly; it would fail in step 254 ev=build_eventor(param=-9)55 run_id=ev.run_id56 ev.run()57 ev.close()58 59 # rerun in recovery60 ev=build_eventor(evr.RUN_RECOVER,param=9,run_id=run_id)61 ev.run()62 ev.close()63 64 65 if__name__=='__main__':66 construct_and_run()

示例输出

 1 [ 2016-12-07 08:37:53,541 ][ INFO ][ Eventor store file: /eventor/example/runly03.run.db ]
 2 [ 2016-12-07 08:37:53,586 ][ INFO ][ [ Step s1/3 ] Trying to run ]
 3 [ 2016-12-07 08:37:53,588 ][ INFO ][ Square of 3 is 9 ]
 4 [ 2016-12-07 08:37:53,588 ][ INFO ][ [ Step s1/3 ] Completed, status: TaskStatus.success ]
 5 [ 2016-12-07 08:37:55,644 ][ INFO ][ [ Step s2/3 ] Trying to run ]
 6 [ 2016-12-07 08:37:55,647 ][ INFO ][ [ Step s2/3 ] Completed, status: TaskStatus.failure ]
 7 [ 2016-12-07 08:37:56,663 ][ ERROR ][ Exception in run_action:
 8     <Task(id='2', step_id='s2', sequence='3', recovery='0', pid='8112', status='TaskStatus.failure', created='2016-12-07 14:37:55.625870', updated='2016-12-07 14:37:55.633819')> ]
 9 [ 2016-12-07 08:37:56,663 ][ ERROR ][ ValueError('math domain error',) ]
10 [ 2016-12-07 08:37:56,663 ][ ERROR ][ File "/sand/eventor/eventor/main.py", line 62, in task_wrapper
11             result=step(seq_path=task.sequence)
12 File "/sand/eventor/eventor/step.py", line 82, in __call__
13             result=func(*func_args, **func_kwargs)
14 File "/eventor/example/runly03.py", line 66, in square_root
15         y=math.sqrt(x) ]
16 [ 2016-12-07 08:37:56,663 ][ INFO ][ Stopping running processes ]
17 [ 2016-12-07 08:37:56,667 ][ INFO ][ Processing finished with: failure ]
18 [ 2016-12-07 08:37:56,670 ][ INFO ][ Eventor store file: /eventor/example/runly03.run.db ]
19 [ 2016-12-07 08:37:57,736 ][ INFO ][ [ Step s2/3 ] Trying to run ]
20 [ 2016-12-07 08:37:57,739 ][ INFO ][ Square root of 9 is 3.0 ]
21 [ 2016-12-07 08:37:57,739 ][ INFO ][ [ Step s2/3 ] Completed, status: TaskStatus.success ]
22 [ 2016-12-07 08:38:00,798 ][ INFO ][ [ Step s3/3 ] Trying to run ]
23 [ 2016-12-07 08:38:00,800 ][ INFO ][ dividing 9 by 3 is 3.0 ]
24 [ 2016-12-07 08:38:00,800 ][ INFO ][ [ Step s3/3 ] Completed, status: TaskStatus.success ]
25 [ 2016-12-07 08:38:01,824 ][ INFO ][ Processing finished with: success ]

示例突出显示

The function build_flow (code line 24) build an Eventor flow using three functions defined in advance. Since no specific store is provided in Eventor instantiation, a default runner store is assigned (code line 25). In this build, step s2 (lines 30-35) is being set with recovery directives.

The first build and run is done in lines 47-48. In this run, a parameter that would cause the second step to fail is being passed. As a result, flow fails. Output lines 1-17 is associated with the first run.

The second build and run is then initiated. In this run, parameter is set to a value that would pass step s2 and run mode is set to recovery (code lines 51-52). Eventor skips successful steps and start executing from failed steps onwards. Output lines 18-25 reflects successful second run.

Delayed Associations

There are situations in which it is desire to hold off activating a task. This behavior is captured in Eventor as a delayed association.

Associations can be made delayed. Assuming source event is associated to target event with time delay. When source event is triggered, Eventor will wait time delay seconds before triggering target event.

In such situations, it sometimes desire to run Eventor engine in specific period on a time line instead of continuously. For example, if Eventor is synchronizing activities that has 6 hours association delay. Instead of running Eventor continuously, it can be set to run every 5 minutes, and save computing resources on the side.

With delayed associations, Eventor can run in continue run mode (RunMode.continue_). When running in continue, Eventor will pick up from where it left last run.

The following example present delayed association with continue run mode.

延迟示例

 1 import eventor as evr
 2 import logging
 3 import os
 4 import time
 5  6 appname = os.path.basename(__file__)
 7 logger = logging.getLogger(appname)
 8  9 def prog(progname):
10     logger.info("doing what %s is doing" % progname)
11     logger.info("EVENTOR_STEP_SEQUENCE: %s" % os.getenv("EVENTOR_STEP_SEQUENCE"))
12     return progname
13 14 15 def build_flow(run_mode):
16     ev = evr.Eventor(name=appname, run_mode=run_mode,)
17 18     ev1s = ev.add_event('run_step1')
19     ev2s = ev.add_event('run_step2')
20     ev3s = ev.add_event('run_step3')
21 22     s1 = ev.add_step('s1', func=prog, kwargs={'progname': 'prog1'}, triggers={evr.STEP_SUCCESS: (ev2s,)})
23     s2 = ev.add_step('s2', func=prog, kwargs={'progname': 'prog2'}, triggers={evr.STEP_SUCCESS: (ev3s,)})
24     s3 = ev.add_step('s3', func=prog, kwargs={'progname': 'prog3'},)
25 26     ev.add_assoc(ev1s, s1, delay=0)
27     ev.add_assoc(ev2s, s2, delay=10)
28     ev.add_assoc(ev3s, s3, delay=10)
29 30     ev.trigger_event(ev1s, 1)
31     return ev
32 33 34 def construct_and_run():
35     ev = build_flow(run_mode=evr.RUN_RESTART)
36     ev.run()
37     ev.close()
38 39 if __name__ == '__main__':
40     construct_and_run()

示例输出

 1 [ 2017-08-16,16:31:29.277048 ][ Task-s1(1)  ][ INFO    ][ [ Step s1/1 ] Trying to run ]
 2 [ 2017-08-16,16:31:29.277903 ][ Task-s1(1)  ][ INFO    ][ doing what prog1 is doing ]
 3 [ 2017-08-16,16:31:29.278114 ][ Task-s1(1)  ][ INFO    ][ EVENTOR_STEP_SEQUENCE: 1 ]
 4 [ 2017-08-16,16:31:29.278360 ][ Task-s1(1)  ][ INFO    ][ [ Step s1/1 ] Completed, status: TaskStatus.success ]
 5 [ 2017-08-16,16:31:41.028196 ][ Task-s2(1)  ][ INFO    ][ [ Step s2/1 ] Trying to run ]
 6 [ 2017-08-16,16:31:41.029191 ][ Task-s2(1)  ][ INFO    ][ doing what prog2 is doing ]
 7 [ 2017-08-16,16:31:41.029429 ][ Task-s2(1)  ][ INFO    ][ EVENTOR_STEP_SEQUENCE: 1 ]
 8 [ 2017-08-16,16:31:41.029697 ][ Task-s2(1)  ][ INFO    ][ [ Step s2/1 ] Completed, status: TaskStatus.success ]
 9 [ 2017-08-16,16:32:02.931265 ][ Task-s3(1)  ][ INFO    ][ [ Step s3/1 ] Trying to run ]
10 [ 2017-08-16,16:32:02.932407 ][ Task-s3(1)  ][ INFO    ][ doing what prog3 is doing ]
11 [ 2017-08-16,16:32:02.932661 ][ Task-s3(1)  ][ INFO    ][ EVENTOR_STEP_SEQUENCE: 1 ]
12 [ 2017-08-16,16:32:02.932940 ][ Task-s3(1)  ][ INFO    ][ [ Step s3/1 ] Completed, status: TaskStatus.success ]
13 [ 2017-08-16,16:32:03.014584 ][ MainProcess ][ INFO    ][ Processing finished with: success; outstanding tasks: 0 ]

示例突出显示

The example program builds and runs Eventor sequence 4 times. The build involves three tasks that would run sequentially. They are associated to each other with delay of 10 seconds each (lines 26 and 28.)

The first time, sequence is build with restart run mode (line 35). In this case, the sequence is initiated. The next four runs are in continue run mode (line 48). Each of those run continue its preceding run. To have it show the point, a varying delay is introduced between runs (lines 46-47).

Each run limits the number of loop to a single loop (lines 40 and 50). A single loop entails Eventor executing triggers and tasks until there is none to execute. It may be though that there are still outstanding delayed association to act upon.

This behavior is different than continuous run (using max_loops=-1), which is the default. In such run, Eventor will continue to loop until there are no triggers, tasks, and delayed association to process.

Eventor runs can be observed in example output lines 1-5, 6, 7-11, 12, and 13-17 each. Note that the second and forth runs had not trigger to execute on. The associated tasks’ delays was not yet matured.

Resources

add_step allows association of step with resources. If acquires argument is provided, before step starts, Eventor will attempt to reserve resources. Step will be executed only when resources are secured.

When release argument is provided, resources resources listed as its value will be released when step is done. If release is None, whatever resources stated by acquires would be released. If the empty list is set as value, no resource would be released.

To use resources, program to use Resource and ResourcePool from acris.virtual_resource_pool. Example for such definitions are below.

资源定义示例

 1 importeventorasevr 2 fromacrisimportvirtual_resource_poolasvrp 3  4 classResources1(vrp.Resource):pass 5 classResources2(vrp.Resource):pass 6  7 rp1=vrp.ResourcePool('RP1',resource_cls=Resources1,policy={'resource_limit':2,}).load() 8 rp2=vrp.ResourcePool('RP2',resource_cls=Resources2,policy={'resource_limit':2,}).load() 9 10 ev=evr.Eventor()11 12 s1=ev.add_step('s0.s00.s1',func=prog,kwargs={'progname':'prog1'},acquires=[(rp2,1),],)

Distributed Steps

Eventor程序可以在集群环境中工作在这种安排中,可以定义步骤以在集群中的不同节点上运行。这是可能的:

  1. SSH is defined among cluster nodes.
  2. Eventor DB is shared among cluster nodes.
  3. Program environment is the seamlessly-the-same among cluster nodes.

工作原理

eventor将从一个主机server启动。然后,它将在与程序clients相关联的每个主机上启动相同的程序。client程序将跳过starting步骤(没有步骤的步骤)

群集ssh访问

在分布式环境中工作时,eventor假设在参与的主机之间正确设置了ssh。

要允许不自动执行带有.profile(或.bash_profile)的ssh run命令,请在RSA key in.ssh/authorizedkeys之前添加以下内容

command"if [[ \"x${SSH_ORIGINAL_COMMAND}x\" != \"xx\" ]]; then source ~/.profile; eval \"${SSH_ORIGINAL_COMMAND}\"; else /bin/bash --login; fi;"<key>

数据库

eventor程序将在与程序相关的所有集群节点上启动。

TODOs

The following is some of the major tasks intended to be completed into this product.

  1. asynchronous tasks: embed mechanism to launch asynchronous tasks.
  2. remote callback mechanisms: allow remote asynchronous tasks communicate with Eventor (TCP/IP, HTTP, etc.)
  3. virtual resources shared across distributed environment.
  4. improve SSHPipe to better indicate SSH channel was established.

Change log

5.0

  1. added database configuration allowing the use of SqlAlchemy database engines.
  2. added shared_db to indicate db is shared among multiple programs and runs.
  3. added run_id as unique identifier for program run (not to be confused with recovery).
  4. improved documentation to reflect the need for mp.freeze_support() and mp.set_start_method(‘spawn’).
  5. added dependency on namedlist, and PyYAML, packages.
  6. bug fix in delay.

5.1

  1. SSH remote invocation of steps.
  2. use of socket based logging.
  3. centralized logging of remote agents in prime server.
  4. Added {envvar_prefix}LOGGER_NAME to allow step logger to be set appropriately.

Additional Information

  1. Eventor github project (Eventor github project) has additional examples with more complicated flows.
  2. SSH Pipe blog clarifying the mechanism using by Eventor for remote steps and centralized logging.

欢迎加入QQ群-->: 979659372 Python中文网_新手群

推荐PyPI第三方库


热门话题
java连接在一个屏幕上成功下载,在第二个屏幕上用几乎相同的代码获得错误   java调用super。超级的方法,跳过超级。方法   使用Web服务连接到sharepoint 2013的Java应用程序   java我无法正确呈现editText   httpurlconnection如何在java中检查url连接状态   java Spring Security可以为同一用户接受多个密码吗?   java如何在PreparedStatement中使用自动生成的@Id?   java每个数组表示一个位模式   java我不确定如何记录鼠标在某个区域被点击的次数   spring如何解决:java。lang.NoSuchMethodError:javax。坚持不懈实体管理器。createStoredProcedureQuery(Ljava/lang/String;)   java如何为blackberry中listfield项内的不同字段触发事件   安卓使用Proguard混淆java代码   java在grails 2中与多个数据源有一个和一个域关联。十、   java在尝试在单击按钮时返回combobox值时一直出错   java我可以在setter中使用@Resource注释而不是字段吗?   java Eclipse调试步进不工作   java比较相同对象的两个表并选择不同的表