ofbiz初学者指南 笔记

ofbiz ftl入门:

    ftl嵌入screen: ${screens.render("component://learning/widget/learning/

LearningScreens.xml#\editcontactmech.extend")}

    ftl作为装饰器: <title>${sections.render("title")}</title>

    <platform-specific><html>

<html-template-decorator location="component://learning/webapp/

learning/includes/header.ftl">

<html-template-decorator-section name="title">

<decorator-section-include name="title"/>

</html-template-decorator-section>

</html-template-decorator>

</html></platform-specific>

暂且认为localDispatcherName必须和webapp的挂载点相同. 

preprocessor:元素: 用来定义在每个请求都会执行的事件. 该事件位于security之后.

定义Handler: 

默认的ofbiz将会外键自动建立索引. datasource的属性use-foreign-key-indices 设置为true. 

最好不要再外键上建立索引. 因为ofbiz会自动创建. 有的数据库自持自动创建外键的索引, 此时可以将use-foreign-key-indices设置为false, 那样的就不会为相同的字段建立两遍索引. 


one-to-one关系: 

通过<relation type="one">定义. The foreign key must contain the same fields in the same order as the primary key

fields in the referenced entity. Every foreign key must either reference a unique

record in the referenced entity, or be null and reference nothing at all. 


Java代码获取内置对象:

HttpSession session = request.getSession();

List UtlavailableLocales = UtilMisc.availableLocales();

Locale locale = UtilHttp.getLocale(request);

GenericDelegator delegator = (GenericDelegator)request.getAttribute("delegator");

LocalDispatcher dispatcher = (LocalDispatcher)request.getAttribute("dispatcher");

Security security = (Security)request.getAttribute("security");

GenericValue userLogin = (GenericValue)session.getAttribute("userLogin");

通过userLogin获得Party记录: 

GenericValue userLogin =

(GenericValue)session.getAttribute("userLogin");

GenericValue party =null;

Try{

party = userLogin.getRelatedOne("Party");

}catch(GenericEntityException e){

Debug.logError(e, module);

}

在Java代码里控制安全性: 

public static String checkAccess(HttpServletRequest request,  HttpServletResponse response){

    Security security = (Security)request.getAttribute("security");

    String key = "_EVENT_MESSAGE_";

    if (security.hasPermission("LEARN_VIEW", request.getSession()))

{

request.setAttribute(key, "You have access!");

}

else {

request.setAttribute(key, "You DO NOT have access! You are denied!");

}

return "success";

}

Ofbiz消息占位符常用的有如下几个: 

    _EVENT_MESSAGE_

    _EVENT_MESSAGE_LIST_

    _ERROR_MESSAGE_

    _ERROR_MESSAGE_LIST_

    _ERROR_MESSAGE_MAP_

Widget Engine将前四个占位符里的消息放在分别放在两个列表里, 分别是"eventMesssageList"和"errorMessageList", 这两个列表又被放在了widget环境的context对象中. _ERROR_MESSAGE_MAP_主要是被Service Engine使用, 不被Widget Engine处理. 


处理请求参数: 

使用 org.ofbiz.base.util.UtilHttp处理请求参数: 

public static String receiveParams(HttpServletRequest request, HttpServletResponse response){

Map parameters = UtilHttp.getParameterMap(request);

String name = "";

String firstName = (String)parameters.get("firstName");

String lastName = (String)parameters.get("lastName");

if (firstName != null) name += firstName;

if (lastName != null) {

if (name != null) name += " ";

name += lastName;

}

String message = "Welcome, " + name + "!";

request.setAttribute("_EVENT_MESSAGE_", message);

return "success";

}


程序中读取国际化配置文件: 

public static String welcomeVisitor(HttpServletRequest request, HttpServletResponse response){

String resource = "LearningUiLabels";

Locale locale = UtilHttp.getLocale(request);

String message = UtilProperties.getMessage(resource, "LearningWelcomeMsg", locale);

String smallTalk = UtilProperties.getMessage(resource, "LearningSmallTalk", locale);

Map parameters = UtilHttp.getParameterMap(request);

String name = "";

String firstName = (String)parameters.get("firstName");

String lastName = (String)parameters.get("lastName");

if (firstName != null) name += firstName;

if (lastName != null) {

if (name != null) name += " ";

name += lastName;

}

message += " " + name + "!";

message += "<br>" + smallTalk;

request.setAttribute("_EVENT_MESSAGE_", message);

return "success";

}

使用参数化的消息: 

可以在属性文件中这样定义消息: LearningWelcomeMsg=Welcome to France! {0}!

通过如下方法获得消息: String message = UtilProperties.getMessage(resource, "LearningWelcomeMsg", new String[] {name}, locale);

国际化时, 如果ofbiz没有找到最精确的语言匹配, 那么它将寻找稍微更通用的版本. 

如果请求的是更通用的语言文字版本, 如果没有找到更通用的版本, 但是此时存在某个特定的版本, 那么使用该特定的版本. 

Java 操作数据库: 

GenericDelegator delegator = (GenericDelegator)request.getAttribute("delegator");

List postalAddresses = null;

try    {

postalAddresses = delegator.findAll("PostalAddress");

}catch(GenericEntityException e){

Debug.logError(e, module);

return "error";

}

服务

服务必须返回一个map, 该map至少包含一个条目, 这个条目的key是"responseMessage" 参见org.ofbiz.service.ModelService.RESPONSE_MESSAGE), 对应的值应该是如下几种之一: 

success or ModelService.RESPOND_SUCCESS

error or ModelService.RESPOND_ERROR

fail or ModelService.RESPOND_FAIL

ServiceUtil.returnSuccess()可以帮我们构建如上的消息结构. 

另外一个常用的消息对象是"successMessage".  我们可以使用ServiceUtil.returnSuccess("Some

message")构建. 

ServiceUtil类可以使我们从消息的格式规范中解脱出来. 

要想使用service的事件类型, 我们需要在controller.xml中添加如下处理器: 

    <handler name="service" type="request" class="org.ofbiz.webapp.event.ServiceEventHandler"/>

    <handler name="service-multi" type="request" class="org.ofbiz.webapp.event.ServiceMultiEventHandler"/>

Service 的参数: 
    包含输入参数和输出参数. 
    用户通过表单像service提交的参数, 如果没有在service的参数列表中声明, 那么没有声明的参数将会被忽略. 其他的参数将会被转化为Map, 传递给我们定义的静态函数(作为第二个参数). 
    参考代码如下: 
    public static Map handleParameters(DispatchContext dctx, Map  context){

String firstName = (String)context.get("firstName");

String lastName = (String)context.get("lastName");

String planetId= (String)context.get("planetId");
String message = "firstName: " + firstName + "<br/>";
message = message + "lastName: " + lastName + "<br/>";
message = message + "planetId: " + planetId;
Map resultMap = ServiceUtil.returnSuccess(message);
return resultMap;
}

输出参数(OUT): 

    service可以带有输出参数. 当作为controller的事件时, service声明的参数会被无声的丢弃.

    将输出的参数, 放置在输出的map中. 例如如下:

          Map resultMap = ServiceUtil.returnSuccess(message);

resultMap.put("fullName", firstName + " " + lastName);

return resultMap;

双向的参数

    service可能改变参数的值然后输出. 为了防止多次定义同一个参数, 我们可以使用INOUT类型的参数. 

一些默认存在的参数

responseMessage

errorMessage

errorMessageList

successMessage

successMessageList

userLogin

locale

其中responseMessage

errorMessage

errorMessageList

successMessage

successMessageList

是返回消息必须的.  在所有的有效性检测中, 他们必须是被允许的. 

userLogin主要用来进行身份验证和权限检测. 

locale主要用来国际化. 

可选和必须参数

Service Engine根据service的定义对输入和输出参数进行有效性检测. 当前的事务会被标记为"rollback", 这意味着这个事务中对数据库做的任何改变包括调用别的service做的改变不会被提交. 

如果optional 属性时false的(即参数定义为不可选的), 如果缺少这个参数, 将会导致校验出错, service会返回错误. 

例如如下:

<attribute name="fullName" type="String" mode="INOUT"

optional="false"/>

fullName必须作为参数传递给service, 同时service的resultMap中必须包含该参数. 

Dispatch Context

当service作为event被使用的时候, userLogin 和 locale对象被添加到context map中. 

DispatchContext还包含以下我们访问数据库和调用其他service的工具: 

我们可以在Java code中获取如下的方法: 

GenericValue userLogin = (GenericValue)context.get("userLogin");

Locale locale = (Locale)context.get("locale");

GenericDelegator delegator = dctx.getDelegator();

LocalDispatcher dispatcher = dctx.getDispatcher;

Service engine 不依赖于HTTPServletRequest或HTTPServletResponse对象. 因此我们可以脱离web 环境, 远程调用service或者指定"离线"的运行计划. 

Service 安全和权限控制: 

public static Map serviceWithAuth(DispatchContext dctx, Map context){

    Security security = dctx.getSecurity();

Map resultMap = null;

if (context.get("userLogin") == null || !security.hasPermission("LEARN_VIEW", (GenericValue)context.get("userLogin"))) {

resultMap = ServiceUtil.returnError("You have no access here. You're not welcome!");

}

else {

resultMap = ServiceUtil.returnSuccess("Welcome! You have access!");

}

return resultMap;

}

在Java代码中调用Service: 

在代码中使用service我们需要借助于dispatcher对象, 是org.ofbiz.service.ServiceDispatcher类的一个实例.  可以从DispatchContext中获得.  因此我们可以在service中调用其他服务. 

public static Map callingServiceOne(DispatchContext dctx, Map context){

LocalDispatcher dispatcher = dctx.getDispatcher();

Map resultMap = null;

String firstName = (String)context.get("firstName");

String lastName = (String)context.get("lastName");

String planetId = (String)context.get("planetId");

GenericValue userLogin = (GenericValue)context.get("userLogin");

Locale locale = (Locale)context.get("locale");

Map serviceTwoCtx = UtilMisc.toMap("planetId", planetId, "userLogin", userLogin, "locale", locale);

try{

resultMap = dispatcher.runSync("learningCallingServiceTwo",  serviceTwoCtx);

}catch(GenericServiceException e)

{

Debug.logError(e, module);

}

resultMap.put("fullName", firstName + " " + lastName);

return resultMap;

}

public static Map callingServiceTwo(DispatchContext dctx, Map context){

String planetId = (String)context.get("planetId");

Map resultMap = null;

if(planetId.equals("EARTH")){

resultMap = ServiceUtil.returnSuccess("This planet is Earth");

}else{

resultMap = ServiceUtil.returnError("This planet is NOT

Earth");

}

return resultMap;

}

调用另外一个代码service的代码: 

    resultMap = dispatcher.runSync("learningCallingServiceTwo", serviceTwoCtx);

运行服务的三种方式: 

runSync: 同步调用一个服务, 结果以map的形式返回.

runSyncIgnore: 同步调用一个服务. 忽略结果.

runAsync: 异步的调用service.

实现接口

定义接口:  将engine设置为interface

<service name="learningInterface" engine="interface">

<description>Interface to describe base parameters for Learning

Services</description>

<attribute name="firstName" type="String" mode="IN"

optional="false"/>

<attribute name="lastName" type="String" mode="IN"

optional="false"/>

<attribute name="planetId" type="String" mode="IN"

optional="false"/>

<attribute name="fullName" type="String" mode="OUT"

optional="true"/>

</service>

实现的service如下: 

<service name="learningCallingServiceOne" engine="java"

location="org.ofbiz.learning.learning.LearningServices"

invoke="callingServiceOne">

<description>First Service Called From The Controller

</description>

        <implements service="learningInterface"/>

</service>

重写interface的属性: 

<service name="learningCallingServiceOne" engine="java"

location="org.ofbiz.learning.learning.LearningServices"

invoke="callingServiceOne">

<description>First Service Called From The Controller

</description>

<implements service="learningInterface"/>

<attribute name="planetId" type="String" mode="IN"

optional="false"/>

</service>

同步调用服务和异步调用服务: 

同步调用服务时, 同步服务和当前的服务在相同的进程中运行. 当前的进程将会等待进程的运行结束然后进行接下来的操作.  主调服务可以从被调服务中获得信息. 

异步调用将会为被调用的service新建进程. 主调服务不能够获得被调进程的任何信息.  被调服务发生错误不会影响主调服务. 

最好的例子是sendOrderConfirmation

实际上被异步调用的服务被加入到了Job Scheduler中. Job Scheduler负责调用处于等待队列中的服务.

可以在webtools的Job List中看到任务的列表: 

这些任务的Pool被默认设置为pool.  对于过个ofbiz实例连接到同一个database

的情况, 这样是非常重要的.  多个ofbiz实例中选定一个专门来运行特定的任务. 虽然所有的ofbiz实例都可以运行job scheduler. 我们可以通过配置改变. 

在frame/service/config/serviceengine.xml中将"send-to-pool"和  <run-from-pool name="pool"/>

<!-- Thread pool configuration (max/min threads, uses to live and time to live) -->

        <thread-pool send-to-pool="pool"

                     purge-job-days="4"

                     failed-retry-min="3"

                     ttl="18000000"

                     wait-millis="750"

                     jobs="10"

                     min-threads="5"

                     max-threads="15"

                     poll-enabled="true"

                     poll-db-millis="20000"

                     >

            <run-from-pool name="pool"/>

        </thread-pool>

我们这样可以确保当前ofbiz创建的job运行在这个实例中. 
快速运行一个service: 
可以通过Webtools快速运行ofbiz服务. 
Service的命名和引用: 
在整个程序中ofbiz服务的名字必须是唯一的. 因为我们引用service的时候不必要指定service的位置.  如果存在重名的情况, 我们就不能保证被执行的服务是我们期望的那一个. 

尽量给service起见名知意的名字. 尽量使用description标签, 以方便其他的开发人员调用你写的service. 

Event Condition Actions(ECA)(时间-条件-动作)

ECA表示一个过程的准则.  Event是起因或触发, 我们通过condition来判断是否进一步执行actions. actions最终导致修改或改变.  有两种类型的ECA ,分别是SECA(服务ECA)和EECA(实体ECA).

Service Event Condition Actions(SECAs)

对于SECA, event是服务被执行.  条件是参数是否与给定的值相等. 动作是调用其他的service. 

SECA定义在servicedef文件夹的seca.xml文件中. 

最简单的SECA的例子如下:(位于applications\order\servicedef\secas.xml)

<eca service="changeOrderStatus" event="commit"

run-on-error="false">

<condition field-name="statusId" operator="equals"

value="ORDER_CANCELLED"/>

<action service="releaseOrderPayments" mode="sync"/>

</eca>

Entity Event Condition Actions(EECA) 

对于EECA, 事件是对entity的操作, 动作是调用service. 

EECA和entity定义在同一个文件夹下的eecas.xml中. 

EECA用于不使用service对entity进行操作的情况(不然可以用事SECA),  或者您期望不管任何service对实体进行了操作, 都要执行一系列的动作. 

简单参考如下的eca: 

<eca entity="Product" operation="create-store" event="return">

<condition field-name="autoCreateKeywords" operator="not-equals" value="N"/>

<action service="indexProductKeywords" mode="sync" value-attr="productInstance"/>

</eca>

这个ECA执行如下的操作: 当创建或更新Product的记录时, 只要autoCreateKeywords字段的值不为N, 就会同步的调用indexProductKeywords. 

Operation包含如下几种: 

create

store

remove

find

create-store (create or store/update)

create-remove

store-remove

create-store-remove

any

可以使用的event类型:  

- validate

- run

- return

- cache-check

- cache-put

- cache-clear

要想正常使用, 必须在ofbiz-component.xml中添加如下的声明: 

<entity-resource type="eca" loader="main" location="entitydef/eecas.xml"/>

11 Permissions and Service Engine(服务引擎的权限控制)

简单的权限: 

service在定义的时候, 可以指定需要的权限. dispatcher在调用service前会检查这些权限. 

简单的定义如下: 

<service name="learningCallingServiceOneWithPermission" engine="java" location="org.ofbiz.learning.learning.LearningServices" invoke="callingServiceOne">

    <description>First Service Called From The Controller</description>

<required-permissions join-type="OR">

<check-permission permission="LEARN_VIEW"/>

</required-permissions>

<implements service="learningInterface"/>

</service>

<required-permissions>元素只有一个join-type属性. join-type可以取值为ANY

或OR.  他们来确定权限检查是如何组织在一起的. 

<required-permissions>可以包含多个<check-permission> and <check-role-member> 元素. 

两段式权限和特殊的"_ADMIN"权限

两段式的权限是指形式为<mainpermission>_<action>权限字符串. 

例如LEARN_VIEW表示LEARN权限和VIEW动作. 

上例中的check-permission权限也可以这样写: 

<check-permission permission="LEARN" action="VIEW"/>

两种写法完全等价. 

只要在check-permission里出现了action属性, 那么权限就是两段式的权限. 

两段式权限对"全局权限"有特殊的处理, 例如"super user". 全局权限或者超级权限是是允许访问程序所有模块的权限. 这种权限允许我们只向一个"super user"分配一个超级权限就够了. 而不是分配数千个可能的权限.

如果将service的权限改为简单权限,  拥有_ADMIN权限的用户也会权限检查失败.  因此更加常用两段式的权限控制, 尤其是当我们需要使用_ADMIN特殊权限获得全部权限时. 

Role Checks(角色检查)

除了security检查外还可以进行角色检查. 在典型的系统中, roles和OFBiz的安全组等价. 在基于"角色"管理的安全系统中是将security permissions分组的方法. 

在Ofbiz中, 角色是另外一类的权限. 他们也可以叫做"角色权限", 因为他们的功能和security permission非常像. 但是角色不知是权限这么简单. 在OFBiz中用来确定两个party之间可能的关系. 例如供应商和消费者之间的关系. 

在实践中, 处于安全和访问控制, 我们认为角色是广义的权限. 并且这样的安全组可以很好的进行安全控制, 安全权限将会成为权限控制的最好的方法. 如果我们将这种结构想象成大楼的安保系统, 如果我们是"Employee" 角色, 那么我们就可以进入大楼. 如果属于"LEARNING"安全组就可以让我们大楼的某一层. 如果有"LEARNING_VIEW"权限, 那么我们就可以进入某个具体的房间. 

组合多种检查

<service name="updateProductReview" engine="simple" default-entity-name="ProductReview"

            location="component://product/script/org/ofbiz/product/product/ProductServices.xml" invoke="updateProductReview" auth="true">

        <description>Updates a product review record</description>

        <required-permissions join-type="OR">

            <check-permission permission="CATALOG_UPDATE"/>

            <check-permission permission="CATALOG_ADMIN"/>

        </required-permissions>

        <auto-attributes mode="IN" include="pk" optional="false"/>

        <auto-attributes mode="IN" include="nonpk" optional="true"/>

    </service>

在service的定义中, 我们可以包含多个<required-permissions>,那就意味着实现两层嵌套的权限检查。 

多个<required-permissions>是通过"AND"组合起来的.

<required-permissions join-type="OR">

<check-permission permission="LEARN_VIEW"/>

</required-permissions>

<required-permissions join-type="OR">

<check-role-member role-type="CUSTOMER"/>

</required-permissions>

嵌套的检测之多包含两层. 

Complex Permissions复杂的权限控制:

Complex Permissions and Simple Permissions cannot be combined: 复杂的权限控制和简单的权限控制不能结合使用.

当<required-permissions>和<permission-service>元素同时存在的时候, 只有<permission-service>被执行, <required-permissions>被忽略. 

12 MINI语言

Validating and Converting Fields(表单校验和转化格式)

校验和类型转变是通过Simple Map Processor实现的. 

Simple Map Processor从context Map中取值并把他们转存在另外一个Map, 在转存的过程中进行类型转变和校验. 

通常来说, Simple Map Processors为simple类型的事件准备通过HTML表单或者查询字符串传递过来的参数. 传入的参数通常是字符串型的. 如下的对象类型也可以被Simple Map Processor进行验证和转换. BigDecimals, Doubles, Floats, Longs, Integers, Dates, Times, java.sql.Timestamps, and Booleans

<make-in-string field="expireDate">

<in-field field="expMonth"/>

<constant>/</constant>

<in-field field="expYear"/>

</make-in-string>

<process field="expireDate">

<copy/>

<validate-method method="isDateAfterToday">

<fail-message message="The expiration date is before today"/>

</validate-method>

</process>

<validate-method method="isDateAfterToday">调用了"isDateAfterToday"静态方法. 

这个静态方法是org.ofbiz.base.util.UtilValidate的方法. 

框架会自动的让MINILang能够访问到org.ofbiz.base.util.UtilValidate类. 

<simple-map-processors xmlns:xsi="http://www.w3.org/2001/

XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.ofbiz.org/dtds/simple-methods.xsd">

<simple-map-processor name="createPlanet">

<process field="planetId">

<copy/>

<not-empty>

<fail-message message="Planet Id Cannot Be Empty" />

</not-empty>

</process>

<process field="planetName">

<copy/>

<not-empty>

<fail-message message="Planet Name Cannot Be Empty" />

</not-empty>

</process>

</simple-map-processor>

</simple-map-processors>

<simple-method method-name="createPlanet"

short-description="Creating a Planet">

<call-map-processor xml-resource="org/ofbiz/learning/learning/LearningMapProcs.xml"

processor-name="createPlanet" 

in-map-name="parameters"

out-map-name="context"/>

<check-errors/>

<make-value value-name="newEntity" entity-name="Planet"/>

<set-pk-fields map-name="context" value-name="newEntity"/>

<set-nonpk-fields map-name="context" value-name="newEntity"/>

<create-value value-name="newEntity"/>

</simple-method>

MiniLang调用: 

    调用service: 

            <call-service service-name="ourService" in-map-name="contextMap">

                <result-to-request result-name="exampleId"/>

            </call-service>

    调用Simple方法: 

            <call-simple-method method-name="inlineMethod"/>

    调用Java方法: 

            <call-class-method class-name="org.ofbiz.base.util.UtilValidate"

method-name="isDateAfterToday"

ret-field-name="booleanIsDateAfterToday">

<field field-name="expiryDate" type="String"/>

</call-class-method>

OFBiz外观: 

${ofbizInstallFolder}\framework\images\webapp\images\ecommain.css是商城的主要样式文件.  image组件被放在framework中是因为它为整个程序提供图片、样式、脚本的支持。 当通过product上传的图片也存放在这里, 因此这个目录可能变得非常庞大, 不过我们可以轻松的改变这个路径, 甚至将其配置在不同的服务器上。 

OFBiz可以为不同的网站提供不同的产品商店, 每个商店拥有不同的分类和产品. 默认, ecommerce组件被当绑定到web.xml中参数websiteId所指定的网站.  通过这个参数, ecommerce就可以知道加载哪个产品商店的数据. 

隐藏appbar: 

<webapp name="ecommerce"

title="eCommerce"

server="default-server"

location="webapp/ecommerce"

mount-point="/ecommerce"

app-bar-display="false"/>

可以如此设置: <webapp name="party"

title="Party"

server="default-server"

location="webapp/partymgr"

base-permission="OFBTOOLS,PARTYMGR"

mount-point="/partymgr"/>

使用FreeMaker

ofbiz的转移标签:

<@ofbizUrl>: 可以将<@ofbizUrl>PlanetReview</@ofbizUrl>

转码为/learning/control/PlanetReview

通过使用该标签解析地址我们可以完全不用关心链接是指向https还是http. 只需在controller.xml中配置request-map的security属性. 

OFbiz先检测Website实体中的Port 和Host name设置, 然后检测url.properties中的设置. 

这个标签可以让我们可以在不移动screen或ftl位置,不改变链接地址的情况下引用他们.

我们只需确保controller.xml中有request-map的定义就可以放心的引用他们.

<@ofbizContentUrl>: 和<@ofbizUrl>功能类似. 负责解析链接到正确的image地址. 

很多时候image目录会变得非常庞大, 还有的时候多个OFBiz实例使用同一个image目录. 以上情况下如果我们使用<@ofbizContentUrl>标签的话, 我们只需要在一处告诉Ofbiz image目录的变化. 那就是url.properties中的content.url.prefix.standard属性.

<@ofbizCurrency>: 货币转义标签. 可以方便的我们将数字转变成我们想要的货币标签. 

在FreeMarker中调用Java

FreeMarkerViewHandler为我们创造一个环境, 让我们可以在Service和Event里可以使用如下的OFBiz对象: 

delegator

dispatcher

security

userLogin

session

request

我们可以使用如下的对象来访问请求参数和请求属性:

requestParameters

requestAttributes

sessionAttributes

我们可以在FTL里调用静态的Java方法.

<#assign exampleMap = ${Static["org.ofbiz.base.util.UtilMisc"].toMap

("keyOne", "valueOne", "keyTwo", "valueTwo")} />


OFbiz使用技巧

调试技巧: 

日志输出调试信息

Debug.logVerbose("This is a Verbose Logging Message", module);

下面这些事最常用的方法:

Debug.logVerbose("Message", module);

Debug.logInfo("Message", module);

Debug.logImportant("Message", module);

Debug.logWarning("Message", module);

Debug.logError("Message", module);

Debug.logFatal("Message", module);

MINI语言Log输出: 

<log level="info" message="This is the Debug Message - ${someField}"/>

level可能的取值: verbose, info, important, warning, and error.

Eclipse远程调试: 

去掉上面一行开头的rem. 

rem "%JAVA_HOME%\bin\java" -Xms128M -Xmx512M -XX:MaxPermSize=512m -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005 -jar ofbiz.jar > runtime\logs\console.log

使用Apache HTTP服务器 
什么是mod_proxy_ajp?

mod_proxy_ajp是AJP1.3协议的支持模块, 它可以使web server通过TCP连接和servlet container进行联系. 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值