来源:http://captaincaveman.posterous.com/jbpm-43-spring-3-jboss-jpa-jta-configuration

Many an hour was spent trying to get jBPM 4.3 to run in a Spring application while using a container managed persistence unit defined in persistence.xml.  There's both good news and bad news for those of you wanting to accomplish this.

The bad news is you cannot rely solely on the hibernate session factory created by the container when using jBPM 4.3.  This is because the SpringProcessEngine meant to integrate jBPM 4.3 with Spring assumes the presence of a LocalSessionFactoryBean in the Spring application context.

The good news is you can get jBPM 4.3 to work simply by providing a LocalSessionFactoryBean.  This does not mean your application needs to use this bean.  It only means that jBPM needs to use it.  Your application can still use the container managed JPA persistence unit defined in persistence.xml.  And as long as you are using JTA transactions, everything will be transactional as expected.  Meaning if you complete some task in a task node, and then an exception is thrown by your application code later in the chain, both the changes made by your application code and the changes made by jBPM will be rolled back appropriately.

This solution has been tested on jBoss AS 5.1 GA, 6.0.0.M1, and 6.0.0.M2.

Here is how you configure this to work.

1.  First, the persistence.xml.  Notice that there are no jBPM mapping files specified here:

<?xml version="1.0" encoding="UTF-8" standalone="no"?> 
<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.0" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">

<!-- Define the container managed persistence unit using JTA.  --> 
    <persistence-unit name="persistenceUnit" transaction-type="JTA"> 
        <provider>org.hibernate.ejb.HibernatePersistence</provider> 
         <jta-data-source>java:/SquareOneDataSource</jta-data-source> 
        
        <!-- Any mapping files for the application go here. --> 
        <mapping-file>so.filter.hbm.xml</mapping-file> 
         
        <!-- 
            These have been put in the hibernate session factory 
            inside the applicationContext.xml spring configuration. 
        --> 
        <!-- <mapping-file>jbpm.repository.hbm.xml</mapping-file> 
         <mapping-file>jbpm.execution.hbm.xml</mapping-file> 
        <mapping-file>jbpm.history.hbm.xml</mapping-file> 
        <mapping-file>jbpm.task.hbm.xml</mapping-file> 
        <mapping-file>jbpm.identity.hbm.xml</mapping-file> --> 
         
        <!-- 
            These hibernate properties will be used by your application 
            code since we will inject this persistence unit with @PersistenceContext 
            using Spring.  Spring will use JNDI to lookup the persistence 
             unit and inject it. 
        -->        
        <properties> 
            <property name="jboss.entity.manager.jndi.name" 
                value="java:SquareOnePersistenceUnit" /> 
             <property name="jboss.entity.manager.factory.jndi.name" 
                value="java:SquareOnePersistenceUnitFactory" /> 
             <property name="hibernate.session_factory_name" 
                value="java:SquareOneHibernateSessionFactory" /> 
            
            <!-- Hibernate Configuration Properties --> 
               <property name="hibernate.archive.autodetection" value="class, hbm" /> 
            <property name="hibernate.ejb.naming_strategy" value="org.hibernate.cfg.ImprovedNamingStrategy" /> 
             <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLInnoDBDialect" /> 
            <property name="hibernate.hbm2ddl.auto" value="update" /> 
             <property name="hibernate.show_sql" value="true" /> 
            <property name="hibernate.format_sql" value="true" /> 
            <property name="hibernate.use_sql_comments" value="true" /> 
             <property name="hibernate.default_batch_fetch_size" value="25" /> 
            <property name="hibernate.order_updates" value="true" /> 
            <property name="hibernate.generate_statistics" value="true" /> 
             
            <!-- Hibernate JDBC and Connection Properties --> 
            <property name="hibernate.jdbc.batch_size" value="25" /> 
            <property name="hibernate.connection.autocommit" value="false" /> 
             <property name="hibernate.connection.release_mode" value="auto" /> 
            
            <!-- Hibernate Cache Properties --> 
            <!-- <entry key="hibernate.cache.provider_class" value="net.sf.ehcache.hibernate.SingletonEhCacheProvider" /> --> 
             <!-- <entry key="net.sf.ehcache.configurationResourceName" value="ehcache.xml" /> --> 
            <property name="hibernate.cache.use_query_cache" value="true" /> 
             <property name="hibernate.cache.use_second_level_cache" value="true" /> 
            
            <!-- Hibernate Transaction Properties --> 
            <!-- <property name="hibernate.transaction.factory_class" value="org.hibernate.transaction.JTATransactionFactory" /> --> 
             <property name="hibernate.transaction.manager_lookup_class" value="org.hibernate.transaction.JBossTransactionManagerLookup" /> 
            <property name="hibernate.transaction.auto_close_session" value="false" /> 
             <property name="hibernate.transaction.flush_before_completion" value="false" /> 
            <property name="hibernate.current_session_context_class" value="org.hibernate.context.JTASessionContext" /> 
             
            <!-- Hibernate Search --> 
            <!-- <entry key="hibernate.search.default.directory_provider" value="false" /> 
            <entry key="hibernate.search.default.indexBase" value="C:/Temp/hibernate_test_indexes" /> 
             <entry key="hibernate.search.default.batch.merge_factor" value="10" /> 
            <entry key="hibernate.search.default.batch.max_buffered_docs" value="10" /> 
             <entry key="hibernate.search.default.optimizer.operation_limit" value="500" /> 
            <entry key="hibernate.search.default.optimizer.transaction_limit.max" value="100" /> --> 
         </properties> 
    </persistence-unit> 
</persistence>

2.  Next, the Spring applicationContext.xml.  The LocalSessionFactoryBean will only be used by jBPM:

<?xml version="1.0" encoding="UTF-8" standalone="no"?> 
<beans xmlns="http://www.springframework.org/schema/beans" 
    xmlns:aop="http://www.springframework.org/schema/aop" 
     xmlns:context="http://www.springframework.org/schema/context" 
    xmlns:jee="http://www.springframework.org/schema/jee" 
     xmlns:tx="http://www.springframework.org/schema/tx" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
     xmlns:ehcache="http://www.springmodules.org/schema/ehcache" 
    xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd   
         http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd   
         http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd  
         http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd   
         http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd 
         http://www.springmodules.org/schema/ehcache http://www.springmodules.org/schema/cache/springmodules-ehcache.xsd">

<!-- 
        Activates a load-time weaver for the context. Any bean within the context that 
        implements LoadTimeWeaverAware (such as LocalContainerEntityManagerFactoryBean) 
        will receive a reference to the auto-detected load-time weaver. 
     --> 
    <context:load-time-weaver aspectj-weaving="on" /> 
    
    <!-- Use this to specify exactly which load-time weaver should be used, but 
        it should get auto-detected. --> 
     <!-- <context:load-time-weaver aspectj-weaving="on" weaver-class="org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver" /> --> 
    
    <!-- 
        This will automatically locate any and all property files you have 
         within your classpath, provided they fall under the META-INF/spring 
        directory. The located property files are parsed and their values can 
        then be used within application context files in the form of 
         ${propertyKey}. 
    --> 
    <context:property-placeholder location="classpath*:META-INF/*.properties" />

<!-- 
        Turn on AspectJ @Configurable support. As a result, any time you 
         instantiate an object, Spring will attempt to perform dependency 
        injection on that object. This occurs for instantiation via the "new" 
        keyword, as well as via reflection. This is possible because AspectJ 
         is used to "weave" Roo-based applications at compile time. In effect 
        this feature allows dependency injection of any object at all in your 
        system, which is a very useful feature (without @Configurable you'd 
         only be able to dependency inject objects acquired from Spring or 
        subsequently presented to a specific Spring dependency injection 
        method). Roo applications use this useful feature in a number of 
         areas, such as @PersistenceContext injection into entities. 
    --> 
    <context:spring-configured />

<!-- 
        This declaration will cause Spring to locate every @Component, 
         @Repository and @Service in your application. In practical terms this 
        allows you to write a POJO and then simply annotate the new POJO as an 
        @Service and Spring will automatically detect, instantiate and 
         dependency inject your service at startup time. Importantly, you can 
        then also have your new service injected into any other class that 
        requires it simply by declaring a field for your service inside the 
         relying class and Spring will inject it. 
        
        Furthermore, this turns on @Autowired, @PostConstruct etc support. These 
        annotations allow you to use common Spring and Java Enterprise Edition 
         annotations in your classes without needing to do any special configuration. 
        The most commonly used annotation is @Autowired, which instructs Spring to 
        dependency inject an object into your class. 
     --> 
    <context:component-scan base-package="com.so" /> 
            
    <!-- enables interpretation of the @Required annotation to ensure that dependency injection actually occures --> 
     <bean class="org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor"/>

<!-- enables interpretation of the @PersistenceUnit/@PersistenceContext annotations providing convenient 
         access to EntityManagerFactory/EntityManager --> 
    <!-- <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/> --> 
    
    <!-- 
        Activates various annotations to be detected in bean classes: Spring's 
         @Required and @Autowired, as well as JSR 250's @PostConstruct, 
        @PreDestroy and @Resource (if available) and JPA's @PersistenceContext 
        and @PersistenceUnit (if available). 
    --> 
     <context:annotation-config transaction-manager="transactionManager" />

<!-- 
        Instruct Spring to perform declarative transaction management 
        automatically on annotated classes. 
     --> 
    <tx:annotation-driven mode="aspectj" transaction-manager="transactionManager" /> 
    
    <!-- 
        Instruct Spring to retrieve and apply @AspectJ aspects which are defined 
         as beans in this context (such as the CallMonitoringAspect below). 
    --> 
    <aop:aspectj-autoproxy proxy-target-class="true" /> 
    
    <!-- 
        Post-processor to perform exception translation on @Repository classes (from native 
         exceptions such as JPA PersistenceExceptions to Spring's DataAccessException hierarchy). 
    --> 
    <bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />

<!--  ========================================================================= --> 
    <!-- Entity Manager --> 
    <!--  ========================================================================= --> 
         
    <!-- Container managed data source. -->    
    <jee:jndi-lookup id="dataSource" jndi-name="java:/SquareOneDataSource"/> 
    
    <!-- 
        Spring will inject this container managed persistence 
         unit anywhere you use @PersistenceContext. 
    --> 
    <jee:jndi-lookup id="entityManagerFactory" jndi-name="java:/SquareOnePersistenceUnitFactory" /> 
        
    <!-- JTA transaction manager to be used by everyone! --> 
     <bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"> 
        <property name="transactionManagerName" value="java:/TransactionManager" /> 
         <property name="userTransactionName" value="UserTransaction" /> 
    </bean> 
    
    <!-- 
        The JNDI Hibernate session factory will not work for jBPM 4.3 as jBPM is expecting 
         a Spring LocalSessionFactoryBean. So we specify our mappings here.  This will only 
        be used by jBPM.  Spring will still inject the container managed persistence unit 
        anywhere you use @PersistenceContext. 
     --> 
    <!-- <jee:jndi-lookup id="sessionFactory" jndi-name="java:/SquareOneHibernateSessionFactory" /> --> 
    <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> 
         <!-- <property name="configLocation" value="classpath:/jbpm/jbpm.hibernate.cfg.xml" /> --> 
        <property name="dataSource" ref="dataSource" /> 
        <property name="mappingResources"> 
             <list> 
                <value>jbpm.repository.hbm.xml</value> 
                <value>jbpm.execution.hbm.xml</value> 
                <value>jbpm.history.hbm.xml</value> 
                 <value>jbpm.task.hbm.xml</value> 
                <value>jbpm.identity.hbm.xml</value> 
            </list> 
        </property> 
        <!-- 
            Hibernate properties needed to configure the session factory to use JTA.  This 
             will ensure that jBPM can participate in the application's JTA transactions. 
        --> 
        <property name="hibernateProperties"> 
            <props> 
                <prop key="hibernate.transaction.factory_class">org.hibernate.transaction.JTATransactionFactory</prop> 
                 <prop key="hibernate.transaction.manager_lookup_class">org.hibernate.transaction.JBossTransactionManagerLookup</prop> 
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQLInnoDBDialect</prop> 
                 <prop key="hibernate.connection.datasource">java:/SquareOneDataSource</prop> 
                <prop key="jta.UserTransaction">UserTransaction</prop> 
                <prop key="hibernate.hbm2ddl.auto">update</prop> 
             </props> 
        </property> 
    </bean> 
    
    <!-- jBPM beans. --> 
    <bean id="springHelper" class="org.jbpm.pvm.internal.processengine.SpringHelper" />

<bean id="processEngine" factory-bean="springHelper" factory-method="createProcessEngine" /> 
    
</beans>

3.  The jbpm.cfg.xml:

<?xml version="1.0" encoding="UTF-8"?>

<jbpm-configuration>

<import resource="jbpm.default.cfg.xml" /> 
  <import resource="jbpm.tx.spring.cfg.xml" /> 
  <import resource="jbpm.jpdl.cfg.xml" /> 
   <import resource="jbpm.bpmn.cfg.xml" /> 
  <import resource="jbpm.identity.cfg.xml" /> 
  <import resource="jbpm.businesscalendar.cfg.xml" /> 
  <import resource="jbpm.console.cfg.xml" /> 
   <!-- Commented out for dev environment only. --> 
  <import resource="jbpm.jobexecutor.cfg.xml" /> 
  
  <!-- 
      This is not needed. 
      <process-engine-context> 
        <string name="spring.cfg" value="applicationContext.xml" /> 
       </process-engine-context> 
  -->

</jbpm-configuration>

Comments (16) 
Mar 04, 2010 
Captain Caveman liked this post. 
Mar 04, 2010 
Twitter said... 
Could you please share this sample code with us 
Mar 05, 2010 
Captain Caveman said... 
I'm sorry, what code are you referring to? This post is about how to configure jBPM 4.3 with Spring and a container managed persistence unit. Following the configuration above, you should be able to accomplish this. 
Apr 13, 2010 
Marcel said... 
I see you're using <context:component-scan /> in your configuration. How do you get that to work in jboss? We were not able to get it working due to the VFS. 
Apr 13, 2010 
Captain Caveman said... 
Sorry Marcel, I'm not sure what problems you are having getting it to work. What version of jboss? It works for me on 5.1, 6M1 and 6M2. I do remember an issue with Spring 2.5 and JBoss's classloader that was supposed to have been fixed in Spring 3. Could this be related? I'm using Spring 3. 
Apr 14, 2010 
Marcel said... 
You just answered the question. I guess for spring 3 it is not a problem. We were using spring 2.5.6. 
Apr 14, 2010 
Marcel said... 
When I run your configuration example against spring 2.5.6 with jbpm4.3, jbpm is still looking for the jbpm.hibernate.cfg.xml, eventhough it is configured in the application context in spring. Do we still need this file. Meaning we now have 3 places where we configure hibernate:

org.hibernate.HibernateException: jbpm.hibernate.cfg.xml not found 
at org.hibernate.util.ConfigHelper.getResourceAsStream(ConfigHelper.java:170) 
at org.hibernate.cfg.Configuration.getConfigurationInputStream(Configuration.java:1439) 
at org.hibernate.cfg.Configuration.configure(Configuration.java:1461) 
at org.jbpm.pvm.internal.wire.descriptor.HibernateConfigurationDescriptor$AddCfgResource.apply(HibernateConfigurationDescriptor.java:151)
at org.jbpm.pvm.internal.wire.descriptor.HibernateConfigurationDescriptor.apply(HibernateConfigurationDescriptor.java:90) 
at org.jbpm.pvm.internal.wire.descriptor.HibernateConfigurationDescriptor.initialize(HibernateConfigurationDescriptor.java:76)
at org.jbpm.pvm.internal.wire.WireContext.performInitialization(WireContext.java:533) 
Apr 14, 2010 
Marcel said... 
Sorry, I seem to have following in my jbpm.cfg.xml:

<import />

Of course this should be removed, then it works 
Apr 14, 2010 
Marcel said... 
Sorry, removed by blog:

<import> 
Apr 14, 2010 
Marcel said... 
RRRRR, again removed....

import resource="jbpm.tx.spring.cfg.xml" 
Apr 14, 2010 
Marcel said... 
This is not going very well, it should be:

import resource="jbpm.tx.hibernate.cfg.xml" 
Apr 14, 2010 
Captain Caveman said... 
I'm glad to hear you got it working Marcel. 
Jun 22, 2010 
Cyril said... 
Hello, I would like to know whether you are able to call Spring beans directly from your process using a JUEL value, as in :

<java> 
expr="#{serviceRequest}" method="sendRequestForProduction" 
continue="async" g="442,84,136,52" 
var="previousActionResult"> 
<arg><object /></arg> 
<transition /> 
</java>

jBPM recognizes "serviceRequest" s a Spring bean name, as long as I work with local transactions.

But when I try to use JTA, then jBPM is no longer able to parse "#{serviceRequest}" as a Spring bean. I get the error :

2010-06-22 17:09:44,531 ERROR [org.jbpm.pvm.internal.cmd.ExecuteJobCmd] (pool-9-thread-1) exception while executing 'ExecuteActivityMessage[140003]' 
org.jbpm.api.JbpmException: script evaluation error: javax.el.PropertyNotFoundException: Cannot find property serviceRequest 
at org.jbpm.pvm.internal.script.ScriptManager.evaluate(ScriptManager.java:130) 
at org.jbpm.pvm.internal.script.ScriptManager.evaluate(ScriptManager.java:118) 
at org.jbpm.pvm.internal.script.ScriptManager.evaluateExpression(ScriptManager.java:90) 
at org.jbpm.pvm.internal.wire.descriptor.ObjectDescriptor.construct(ObjectDescriptor.java:180) 
at org.jbpm.pvm.internal.wire.WireContext.construct(WireContext.java:469) 
...

I tried to inject JTA by two different methods : 
1. injected jtaTransactionManager (provided by JBoss) into my LocalSessionFactoryBean :

<bean> 
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> 
<property> 
value="classpath:jbpm.hibernate.cfg.xml" /> 
<property /> 
<property> 
ref="jtaTransactionManagerJBPM"/> 
</bean>

<jee:jndi-lookup />

2. tried a config closer to yours (especially the LocalSessionFactoryBean.

Both attempts produced the same result : everything works, except "serviceRequest" bean is no longer recognized by jBPM.

Other nodes (not injected that way) work properly, JTA transactions do what they are supposed to do, etc.

Thank you if you can help! 
Jun 22, 2010 
Cyril said... 
Oops, XML came out a bit messed up. Hope the problem remains understandable. 
Jan 22, 2011 
Michael said... 
Hi, I want to integarte jbpm3 with jpa, spring and jboss - do you have any example with this maybe? 
Feb 16, 2011 
Andrew Swan said... 
Thanks, this was useful for learning about the JBoss JPA config options.

JBPM 4.3 + Spring 3 + jBoss + JPA + JTA相关推荐

  1. Primefaces,Spring 4 with JPA(Hibernate 4 / EclipseLink)示例教程

    Primefaces,Spring 4 with JPA(Hibernate 4 / EclipseLink)示例教程 Java Persistence API是标准规范.它提供了一个由不同实现者框架 ...

  2. Spring MVC + Hibernate JPA + Bootstrap 搭建的博客系统

    Spring MVC + Hibernate JPA + Bootstrap 搭建的博客系统 Demo 相关阅读: 1.Spring MVC+Hibernate JPA+ Bootstrap 搭建的博 ...

  3. 《Java Web高级编程——涵盖WebSockets、Spring Framework、JPA H

    2019独角兽企业重金招聘Python工程师标准>>> <Java Web高级编程--涵盖WebSockets.Spring Framework.JPA Hibernate和S ...

  4. spring boot 系列之四:spring boot 整合JPA

    上一篇我们讲了spring boot 整合JdbcTemplate来进行数据的持久化, 这篇我们来说下怎么通过spring boot 整合JPA来实现数据的持久化. 一.代码实现 修改pom,引入依赖 ...

  5. Spring Boot集成JPA的Column注解命名字段无效的问题

    偶然发现,Spring Boot集成jpa编写实体类的时候,默认使用的命名策略是下划线分隔的字段命名. Spring Boot版本:1.5.4.release 数据表: id int, userNam ...

  6. 使用PostgreSQL使用Spring Boot和JPA构建基本应用

    "我喜欢编写身份验证和授权代码." 〜从来没有Java开发人员. 厌倦了一次又一次地建立相同的登录屏幕? 尝试使用Okta API进行托管身份验证,授权和多因素身份验证. 每个不平 ...

  7. Spring Boot&JPA&Hibernate&Oracle

    在本教程中,我们将展示如何创建一个Spring Boot应用程序,该应用程序通过Hibernate与Oracle数据源进行通信. 先决条件: Eclipse IDE(最新版本) Maven的4 Jav ...

  8. Spring Boot Data JPA

    Spring Data JPA简介 用来简化创建 JPA 数据访问层和跨存储的持久层功能. Spring Data JPA提供的接口 Repository:最顶层的接口,是一个空的接口,目的是为了统一 ...

  9. Spring ORM示例 - JPA,Hibernate,Transaction

    Spring ORM示例 - JPA,Hibernate,Transaction 欢迎来到Spring ORM示例教程.今天我们将使用Hibernate JPA事务管理来研究Spring ORM示例. ...

最新文章

  1. javascript迭代_探索JavaScript迭代
  2. 面试官问:Integer 如何实现节约内存和提升性能的?
  3. Memcache知识点梳理
  4. git push everything up to date问题解决
  5. Spark Streaming揭秘 Day13 数据安全容错(Driver篇)
  6. AcWing1081.度的数量(数位DP)题解
  7. 昨晚做了个flash
  8. 【脑电信号】基于matlab小波工具箱脑电降噪【含Matlab源码 707期】
  9. mysql 伪哈希_MySQL技巧--伪哈希索引
  10. 基于ssm校园餐厅订餐管理系统获取(java毕业设计)
  11. 正点原子STM32F4探索者开发板HAL库TFT LCD屏幕例程移植到STM32CubeMX+CLion
  12. 一些实用网址 ubuntu截图+屏蔽一些网址+windows7+ubuntu双系统+泰语打字练习
  13. 企业微信java开发demo_微信企业号demo
  14. 打印机接无线共享服务器出现乱码,Ricoh理光复印机网络打印机出乱码的解决办法...
  15. 2023最新淘宝天猫商品销量,宝贝详情,店铺列表信息分析
  16. 苹果编程语言Swift解析:将推动应用开发巨变
  17. SaaS最通俗易懂的解释是什么?看这篇就够了
  18. 20211229[按秩合并并查集 最小生成树][BZOJ4668]冷战
  19. 从控制台输入用户名和密码, 然后 判断输入的用户名是否是@“Frank”, 密码 是否是 @“lanou”, 如果用户名和密码都正确,则输出登录成功, 否则输出登录失败. 提示:
  20. 那些有趣/实用的 Chrome 扩展神器系列(六)

热门文章

  1. 手机python代码查询四六级准考证_【python】【转载】四六级准考证号找回
  2. 国芯网国产芯片精选月刊V20190801 国产芯片 芯片选型 芯片厂家
  3. 志萍版“老友记”英语笔记--很好的英语学习材料
  4. 5折倒计时!7位大牛讲解如何一天掌握物联网全栈开发
  5. 医院信息化升级,一步到位?教你一招
  6. 6月18日IT行业每日精彩微博TOP10
  7. 在linux下QT设计实现视频播放器
  8. jacob读写Excel
  9. LinkedIn开源Dr.elephant,Hadoop爽了
  10. 安卓手机游戏 纪念碑谷1+2+被遗忘海岸+艾达的梦 解锁码