View Javadoc
1   /**
2    * Copyright (c) Glodon Co. Ltd.
3    */
4   package gboat2.base.core.web;
5   
6   import gboat2.base.bridge.GboatAppContext;
7   import gboat2.base.bridge.debug.DefaultDebugHook;
8   import gboat2.base.bridge.exception.DefaultBusinessNestedException;
9   import gboat2.base.core.Activator;
10  import gboat2.base.core.GBoatClassLoader;
11  import gboat2.base.core.GBoatConsistant;
12  import gboat2.base.core.annotation.Business;
13  import gboat2.base.core.annotation.Domain;
14  import gboat2.base.core.annotation.ListDomain;
15  import gboat2.base.core.annotation.NoQuery;
16  import gboat2.base.core.dao.Page;
17  import gboat2.base.core.dao.QuerySupport;
18  import gboat2.base.core.exception.NoPrivilegeException;
19  import gboat2.base.core.service.IBaseService;
20  import gboat2.base.core.service.ModuleService;
21  import gboat2.base.core.util.ActionUtil;
22  import gboat2.base.core.util.SpringContextUtil;
23  import gboat2.base.core.validate.IDataPrivilegeCheckService;
24  import gboat2.base.core.validate.IOperaPrivilegeCheckService;
25  import gboat2.base.core.validate.PrivilegeCheckServiceFactory;
26  
27  import java.io.IOException;
28  import java.lang.reflect.Field;
29  import java.lang.reflect.InvocationTargetException;
30  import java.lang.reflect.Method;
31  import java.lang.reflect.Modifier;
32  import java.util.Collections;
33  import java.util.HashMap;
34  import java.util.LinkedHashMap;
35  import java.util.List;
36  import java.util.Map;
37  import java.util.regex.Matcher;
38  import java.util.regex.Pattern;
39  
40  import javax.annotation.Resource;
41  import javax.persistence.Entity;
42  import javax.persistence.Id;
43  import javax.servlet.ServletContext;
44  import javax.servlet.http.HttpServletRequest;
45  import javax.servlet.http.HttpServletResponse;
46  
47  import org.apache.commons.collections.MapUtils;
48  import org.apache.commons.lang3.StringUtils;
49  import org.apache.commons.lang3.text.WordUtils;
50  import org.apache.struts2.StrutsConstants;
51  import org.apache.struts2.convention.annotation.Result;
52  import org.apache.struts2.convention.annotation.Results;
53  import org.apache.struts2.osgi.OsgiHost;
54  import org.apache.struts2.osgi.StrutsOsgiListener;
55  import org.apache.struts2.osgi.interceptor.ServiceAware;
56  import org.osgi.framework.Bundle;
57  import org.osgi.framework.BundleContext;
58  import org.osgi.framework.FrameworkUtil;
59  import org.slf4j.Logger;
60  import org.slf4j.LoggerFactory;
61  import org.springframework.beans.factory.annotation.Autowired;
62  import org.springframework.util.ReflectionUtils;
63  
64  import com.opensymphony.xwork2.ActionContext;
65  import com.opensymphony.xwork2.ActionSupport;
66  import com.opensymphony.xwork2.inject.Container;
67  import com.opensymphony.xwork2.inject.Inject;
68  
69  /**
70   * @author lysming
71   * 2011-12-6
72   */
73  @SuppressWarnings("unchecked")
74  public abstract class BaseActionSupport extends ActionSupport implements IBaseActionSupport, ServiceAware {
75  
76  	protected static final long serialVersionUID = 1L;
77  	
78  	private static Logger logger = LoggerFactory.getLogger(BaseActionSupport.class);
79  
80  	public static final String CURRENT_BUNDLE_RES_LOCATION = "_curr_bundle_res_location_";
81  	public static final String MIN_SUFFIX = "minSuffix";
82  
83  	public HttpServletRequest request;
84  
85  	public HttpServletResponse response;
86  
87  	// struts-osgi中注入的始终是system-bundle
88  	protected BundleContext context;
89  	
90  	protected Container container;
91  	
92  	/** 是否开启了 Struts 调试模式 */
93  	protected boolean devMode;
94  	
95  	protected boolean flatResultLayout;
96  	
97  	protected String nameSeparator;
98  
99  	protected OsgiHost osgiHost;
100 
101 	protected Page<?> page;
102 
103     protected String SID; 
104     
105     protected Object model;
106 
107     /** 排序字段 */
108     protected String sortField;
109     
110     /** 排序方向(ASC 或 DESC) */
111     protected String sortDirection;
112 
113     /** 需要跳转到的目标页面 */
114     protected String go;
115 
116     /** 当前执行的 Action 所在的 Bundle */
117     private Bundle actionBundle;
118 
119     /**前端每次请求创建Module的id,用于前端取得module对象*/
120     private String _MdId;
121     
122     private Map<String, Object> listParameterMap = null;
123 
124     //权限校验参数
125     private Map<Object,Object> priCheckParams = new HashMap<Object,Object>();
126 
127 	private IBaseService baseServiceInBase;
128 
129 	@Inject
130 	public void setContainer(Container container) {
131 		this.container = container;
132 	}
133 
134     @Inject(StrutsConstants.STRUTS_DEVMODE)
135     public void setDevMode(String mode) {
136         this.devMode = "true".equals(mode);
137     }
138     
139     @Inject("struts.convention.result.flatLayout")
140     public void setFlatResultLayout(String flatResultLayout) {
141         this.flatResultLayout = "true".equals(flatResultLayout);
142     }
143     
144     @Inject("struts.convention.action.name.separator")
145     public void setNameSeparator(String nameSeparator) {
146         this.nameSeparator = nameSeparator;
147     }
148 
149 	/* (non-Javadoc)
150 	 * @see gboat2.base.bridge.action.IBaseAction#delete()
151 	 */
152 	public String delete() {
153 		deleteModel();
154 
155 		// 如果指定了"delete-success"则转向到"delete-success"配置的页面
156 		if (this.getClass().isAnnotationPresent(Results.class)) {
157 			Results rses = this.getClass().getAnnotation(Results.class);
158 			for (Result rs : rses.value()) {
159 				if (rs.name().equals("delete-success")) {
160 					return rs.name();
161 				}
162 			}
163 		}
164 
165 		// 未指定save-success,则转向到列表页面
166 		try {
167 			response.sendRedirect(getDefaultListUri());
168 			return null;
169 		} catch (IOException e) {
170 			e.printStackTrace();
171 		}
172 		return "redirect:" + getDefaultListUri();
173 	}
174 
175 	/**
176 	 * desktop的Ajax删除处理
177 	 * @return null
178 	 */
179 	public String ajaxDelete() {
180 		try {
181 			deleteModel();
182 			GboatAppContext.output(JsonResult.SUCCESS);
183 		} catch (Exception e) {
184 		    GboatAppContext.output(JsonResult.createFailure(e.getMessage()));
185 			logger.error(e.toString(), e);
186 		}
187 		return null;
188 	}
189 
190 	protected final void deleteModel() {
191 		if (null == SID || SID.equals("")) {
192 			throw new RuntimeException("no id is setted to delete.");
193 		} else {
194 			prepareModel();
195 			Object model = getModel();
196 			if (model != null) {
197 				//判断是否存在deleteXxxxx方法
198 				Class<?> domainClass = getDomainClassAnnotationed();
199 				IBaseService service = getServiceAnnotationed();
200 				Class<?> serviceClass = service.getClass();
201 				String methodName = "delete" + domainClass.getName().substring(domainClass.getName().lastIndexOf(".") + 1);
202 				try {
203 					Method method = serviceClass.getMethod(methodName, domainClass);
204 					method.invoke(service, model);
205 					logger.debug("called " + methodName + " ......");
206 				} catch (IllegalArgumentException e) {
207 					throw new RuntimeException(model.getClass().getName() + " is invalid for method '" + methodName + "' in business class");
208 				} catch (IllegalAccessException e) {
209 					throw new RuntimeException("faild to call method '" + methodName + "' in business class");
210 				} catch (InvocationTargetException e) {
211 					throw new RuntimeException("faild to call method '" + methodName + "' in business class");
212 				} catch (Exception e) {
213 					service.delete(model);//如果不存在deleteXxxxx方法,则用默认方法删除
214 				}
215 				postDelete();
216 			}
217 		}
218 	}
219 
220 	protected void postDelete() {
221 
222 	}
223 
224 	public void prepareModel() {
225 		Class<?> domainClass = getDomainClassAnnotationed();
226 		if (domainClass != null) {
227 			if (SID == null || SID.equals("")) {
228 				if (model == null) {
229 					try {
230 						model = domainClass.newInstance();
231 					} catch (Exception e) {
232 						e.printStackTrace();
233 						throw new RuntimeException(e);
234 					}
235 				}
236 			} else {
237 				model = getServiceAnnotationed().get(domainClass, SID);
238 			}
239 		} else {
240 			logger.warn("No @Domain is configured at " + this.getClass().getName());
241 		}
242 	}
243 
244 	/**
245 	 * 获取列表使用的注解ListDomain类
246 	 * 如果使用了@ListDomain(方法级或类级),则使用该注解申明的ListDomain;
247 	 * 如果没有则使用@Domain申明的Domain
248 	 * @return 返回当前action的po class或者vo class
249 	 */
250 	public final Class<?> getDomainClassAnnotationedForList() {
251 		Class<?> clazz = this.getClass();
252 		//判断方法是否使用了@ListDomain注解
253 		String invokeMethod = "";
254 		invokeMethod = ActionContext.getContext().getActionInvocation().getProxy().getMethod();
255 		Method method = ReflectionUtils.findMethod(this.getClass(), invokeMethod);
256 		if (method != null) {
257 			if (method.isAnnotationPresent(ListDomain.class)) {
258 				ListDomain domainAn = (ListDomain) method.getAnnotation(ListDomain.class);
259 				return domainAn.value();
260 			}
261 		}
262 		//检查initXxxx是否使用了@ListDomain注解
263 		Method initMethod = ReflectionUtils.findMethod(this.getClass(), getInitMethodName(invokeMethod), new Class[] { Map.class });
264 		if (initMethod != null && initMethod.isAnnotationPresent(ListDomain.class)) {
265 			ListDomain domainAn = (ListDomain) initMethod.getAnnotation(ListDomain.class);
266 			return domainAn.value();
267 		}
268 		if (clazz.isAnnotationPresent(ListDomain.class)) {
269 			ListDomain domainAn = (ListDomain) clazz.getAnnotation(ListDomain.class);
270 			return domainAn.value();
271 		} else {
272 			Class<?> domain = getDomainClassAnnotationed();
273 			if (domain != null) {
274 				return domain;
275 			} else {
276 				throw new RuntimeException("No @ListDomain or @Domain can be found for " + invokeMethod);
277 			}
278 		}
279 	}
280 
281 	private String getInitMethodName(String invokeMethod) {
282 		char firstChar = Character.toTitleCase(invokeMethod.charAt(0));
283 		String finalMethodName = "init" + firstChar + invokeMethod.substring(1);
284 		return finalMethodName;
285 	}
286 
287 	/**
288 	 * 获取注解配置的domain类
289 	 * 优先级顺序:
290 	 * 		1、方法是否配置@Domain注解;
291 	 * 		2、类是否配置@Domain注解;
292 	 * 		3、是否传递参数DomainClass
293 	 * 		4、从配置的struts.gboat.domain.basePackage路径中找符合命名规范的PO类
294 	 * 
295 	 * @return 返回当前action的po class
296 	 */
297 	public final Class<?> getDomainClassAnnotationed() {
298 		Class<?> domain = null;
299 		Class<?> clazz = this.getClass();
300 
301 		//判断方法是否使用了@Domain注解
302 		try {
303 			String invokeMethod = ActionContext.getContext().getActionInvocation().getProxy().getMethod();
304 			Method method = clazz.getMethod(invokeMethod, new Class[] {});
305 			if (method.isAnnotationPresent(Domain.class)) {
306 				Domain domainAn = (Domain) method.getAnnotation(Domain.class);
307 				return domainAn.value();
308 			}
309 		} catch (Exception ignore) {
310 			//ignore the exception
311 		}
312 
313 		//判断类是否使用了@Domain注解
314 		if (clazz.isAnnotationPresent(Domain.class)) {
315 			Domain domainAn = (Domain) clazz.getAnnotation(Domain.class);
316 			return domainAn.value();
317 		}
318 
319 		//判断是否传递了DomainClass参数
320 		String domainClass = request.getParameter("DomainClass");
321 		if (null != domainClass && !domainClass.equals("")) {
322 			if (domainClass.indexOf(".") == -1) {
323 				// 从配置的domain路径中寻找匹配的PO类,参见struts.xml中的“struts.gboat.domain.basePackage”参数
324 				String domainBasePack = container.getInstance(String.class, GBoatConsistant.GBOAT_ACTION_DOMAIN_PACKAGE);
325 				String[] packs = domainBasePack.split(";|,");
326 				for (int i = 0; i < packs.length; i++) {
327 					try {
328 						domainClass = packs[i] + "." + domainClass;
329 						return Class.forName(domainClass);
330 					} catch (ClassNotFoundException e) {
331 						logger.info(e.getMessage(), e.getCause());
332 					}
333 				}
334 			}
335 
336 			if (domain == null) {
337 				throw new RuntimeException("Invalid domain class passed by parameter[\"DomainClass\"] : " + domainClass);
338 			}
339 		}
340 
341 		// 从配置的domain路径中寻找匹配的PO类,参见struts.xml中的“struts.gboat.domain.basePackage”参数
342 		String domainBasePack = container.getInstance(String.class, GBoatConsistant.GBOAT_ACTION_DOMAIN_PACKAGE);
343 		if (!StringUtils.isEmpty(domainBasePack)) {
344 			String className = "";
345 			// 根据Action名称自动匹配Domain类名
346 			className = this.getClass().getName();
347 			if (className.indexOf(".") != -1) {
348 				className = className.substring(className.lastIndexOf(".") + 1);
349 			}
350 			if (className.endsWith("Action")) {
351 				className = className.substring(0, className.length() - 6);
352 			}
353 			String[] packs = domainBasePack.split(";|,");
354 			for (int i = 0; i < packs.length; i++) {
355 				try {
356 					domainClass = packs[i] + "." + className;
357 					return Class.forName(domainClass);
358 				} catch (ClassNotFoundException e) {
359 					logger.info(e.getMessage(), e.getCause());
360 				}
361 			}
362 			if (domain == null) {
363 				throw new RuntimeException("No class[" + className + "] existed in package[" + domainBasePack
364 						+ "],failed to auto find domain class.\n@Domain should be setted for " + this.getClass()
365 						+ " or passed by parameter \"DomainClass\"");
366 			}
367 		}
368 		return null;
369 	}
370 
371 	private void prepareModelInBase(String methodName) throws Exception {
372 		// 判断是否由BaseAction的方法触发,如果由override的方法触发将不执行Model准备的方法
373 		Class<?> clazz = this.getClass();
374 		try {
375 			Method editMethod = clazz.getMethod(methodName, new Class<?>[] {});
376 			if (editMethod.getDeclaringClass() == BaseActionSupport.class) {
377 				prepareModel();
378 			}
379 		} catch (NoSuchMethodException exception) {
380 			logger.info(exception.getMessage(), exception.getCause());
381 		}
382 	}
383 
384 	/**
385 	 * 根据@Business定义的服务类查找对应的spring bean
386 	 *  
387 	 * @return 返回IBaseService
388 	 */
389 	final public IBaseService getServiceAnnotationed() {
390 		if (baseServiceInBase == null) {
391 			Class<?> clazz = this.getClass();
392 			if (!clazz.isAnnotationPresent(Business.class)) {
393 				// 未配置service类,采用默认的service类
394 				baseServiceInBase = (IBaseService) context.getService(context.getServiceReference(IBaseService.class.getName()));
395 				if (baseServiceInBase == null) {
396 					throw new RuntimeException("@Business havn't setted for " + this.getClass()
397 							+ ". Bean[id='defaultBaseService'] not found in Spring context.");
398 				}
399 			} else {
400 				Business businessAn = (Business) clazz.getAnnotation(Business.class);
401 				// 从spring context中获取配置的service bean
402 				List<?> annotationSerivces = SpringContextUtil.getInstance().getBeansOfType(businessAn.value(), getActionBundle());
403 				if (annotationSerivces.size() == 0) {
404 					throw new RuntimeException("the Business Bean named " + businessAn.value().getName() + " can not be found.");
405 				} else if (annotationSerivces.size() > 1) {
406 					throw new RuntimeException("the Business Bean named " + businessAn.value().getName() + " is not unique.");
407 				}
408 				baseServiceInBase = (IBaseService) annotationSerivces.get(0);
409 			}
410 		}
411 
412 		return baseServiceInBase;
413 	}
414 
415 	final public void prepareEdit() throws Exception {
416 		prepareModelInBase("edit");
417 	}
418 
419 	protected void initEdit() {
420 
421 	}
422 
423 	/**
424 	 * 注册除包含在实体对象即po,vo中枚举类型
425 	 * 使用场景:如果页面中用到的枚举 但此枚举没有出现在po、vo中,则需要覆盖此方法,把当前枚举注册参数enumClass中
426 	 * @param enumClass
427 	 */
428 	public void registerEnumClassNotInEntry(List<Class<? extends Enum<?>>> enumClass) {
429 
430 	}
431 
432 	/* (non-Javadoc)
433 	 * @see gboat2.base.bridge.action.IBaseAction#edit()
434 	 */
435 	public String edit() {
436 		initEdit();
437 		return "edit";
438 	}
439 
440 	final public void prepareView() throws Exception {
441 		prepareModelInBase("view");
442 	}
443 
444 	protected void initView() {
445 
446 	}
447 
448 	public String view() {
449 		initView();
450 		return "view";
451 	}
452 
453 	final public void prepareList() {
454 		
455 	}
456 
457 	/* 
458 	 * 列表查询页面处理方法,需要在子类中使用@ListDomain或@Domain来定义Domain类支持该实现
459 	 * @see gboat2.base.bridge.action.IBaseAction#list()
460 	 */
461 	public String list() {
462 		Map<String,Object> queryMap = getListParameterMap();
463 		initList(queryMap);
464 		
465 		if(StringUtils.isNotBlank(sortField)) {
466 		    if(!"asc".equalsIgnoreCase(sortDirection) && !"desc".equalsIgnoreCase(sortDirection)) {
467 		        sortDirection = "asc";
468 		    }
469 		    String orderBy = sortField + " " + sortDirection;
470 		    String orderBy_old = (String) queryMap.get(QuerySupport.PARAM_ORDERBY);
471 		    if(StringUtils.isNotBlank(orderBy_old)) {
472 		        Matcher matcher = Pattern.compile("\\b" + sortField.trim() + "\\b(\\s*$|\\s+(asc|desc))?", Pattern.CASE_INSENSITIVE).matcher(orderBy_old);
473 		        if(matcher.find()) { // 通过点击表头排序的字段和原先预设的排序字段有重名的情况
474 		            orderBy = orderBy + "," + matcher.replaceFirst("");
475 		        } else {
476 		            orderBy = orderBy + "," + orderBy_old;
477 		        }
478 		    }
479 		    queryMap.put(QuerySupport.PARAM_ORDERBY, orderBy.replaceFirst(",\\s*$", ""));
480 		}
481 		
482 		executeListQuery(queryMap);
483 		postList(page);
484 		
485 		return "list";
486 	}
487 	
488 	public void ajaxListData(){
489 		
490 	}
491 
492 	protected void executeListQuery(Map<String,Object> queryMap) {
493 		if (!getDomainClassAnnotationedForList().getName().equals(NoQuery.class.getName())) {
494 			page = getServiceAnnotationed().getAsPage(queryMap);
495 			// 默认记住页面的路径
496 			page.getPageBean().setCurrentPageUrl(request.getRequestURI());
497 		}
498 	}
499 
500 	protected final Map<String, Object> getListParameterMap() {
501 		if (listParameterMap == null) {
502 			final Map<String,Object> params = request.getParameterMap();
503 			listParameterMap = new HashMap<String, Object>(params);
504 			listParameterMap.put(QuerySupport.PARAM_TABLENAME, getDomainClassAnnotationedForList().getName());
505 			listParameterMap.put(QuerySupport.PARAM_SOURCE_MAP, params);
506 		}
507 		return listParameterMap;
508 	}
509 
510 	protected void initList(Map<String, Object> params) {
511 
512 	}
513 
514 	protected void postList(Page<?> pageinfo) {
515 
516 	}
517 
518 	/* (non-Javadoc)
519 	 * @see gboat2.base.bridge.action.IBaseAction#save()
520 	 */
521 	public String save() {
522 		saveModel();
523 		
524  		// 如果指定了"save-success"则转向到"save-success"配置的页面
525 		if (this.getClass().isAnnotationPresent(Results.class)) {
526 			Results rses = this.getClass().getAnnotation(Results.class);
527 			for (Result rs : rses.value()) {
528 				if (rs.name().equals("save-success")) {
529 					return rs.name();
530 				}
531 			}
532 		}
533 
534 		// 未指定save-success,则转向到默认列表页面
535 		try {
536 			response.sendRedirect(getDefaultListUri());
537 			return null;
538 		} catch (IOException e) {
539 			e.printStackTrace();
540 		}
541 		return  "redirect:" + getDefaultListUri();
542 	}
543 
544 	/**
545 	 * desktop的Ajax保存处理
546 	 * @return null
547 	 */
548 	public String ajaxSave() {
549 	
550 		try {
551 			saveModel();
552 			Object model = getModel();
553 			GboatAppContext.output(JsonResult.createSuccess(new Object[]{ajaxSaveReturn(model)}));
554 		} catch (Exception e) {
555 			logger.error(e.toString(), e);
556 			GboatAppContext.output(JsonResult.createFailure(e.getMessage()));
557 		}
558 		return null;
559 	}
560 
561 	public Object ajaxSaveReturn(Object model) {
562 		return  Collections.singletonMap("SID", getUuid(model));
563 	}
564 
565 	private String getUuid(Object model) {
566 		String uuid = null;
567 		Method[] methods = model.getClass().getMethods();
568 		for (Method method : methods) {
569 			if (method.isAnnotationPresent(Id.class)) {
570 				try {
571 					uuid = (String) method.invoke(model, new Object[] {});
572 				} catch (IllegalArgumentException e) {
573 					e.printStackTrace();
574 				} catch (IllegalAccessException e) {
575 					e.printStackTrace();
576 				} catch (InvocationTargetException e) {
577 					e.printStackTrace();
578 				}
579 				break;
580 			}
581 		}
582 		return uuid;
583 	}
584 
585 	protected final void saveModel() {
586 		initSave();
587 
588 		IBaseService service = getServiceAnnotationed();
589 		Class<?> serviceClass = service.getClass();
590 		Class<?> domainClass = getDomainClassAnnotationed();
591 
592 		Method overrideMethod = ReflectionUtils.findMethod(this.getClass(), "saveModel", new Class[] { domainClass });
593 		if (overrideMethod != null) {
594 			ReflectionUtils.invokeMethod(overrideMethod, this, new Object[] { model });
595 			logger.debug("called saveModel(" + domainClass.getSimpleName() + ")......");
596 		} else {
597 			// 判断主键值是否为空
598 			boolean isPrimaryKeyNull = true;
599 			if (domainClass.isAnnotationPresent(Entity.class)) {
600 				Method[] methods = domainClass.getMethods();
601 				for (Method m : methods) {
602 					if (m.getName().startsWith("get") && m.isAnnotationPresent(Id.class)) {
603 						try {
604 							Object priKey = m.invoke(model, new Object[] {});
605 							if (priKey != null) {
606 								if (priKey instanceof String) {
607 									isPrimaryKeyNull = ((String) priKey).equals("") ? true : false;
608 								} else if (priKey instanceof Integer) {
609 									isPrimaryKeyNull = ((Integer) priKey).intValue() == 0 ? true : false;
610 								} else {
611 									isPrimaryKeyNull = false;
612 								}
613 								break;
614 							}
615 						} catch (Exception e) {
616 							e.printStackTrace();
617 							throw new RuntimeException(e);
618 						}
619 					}
620 				}
621 			} else {
622 				throw new RuntimeException("Now only support Hibernate configured by annotation. No @Entity at " + domainClass.getName());
623 			}
624 
625 			if (isPrimaryKeyNull) {
626 				//判断是否存在saveXxxxx方法
627 				String methodName = "save" + domainClass.getName().substring(domainClass.getName().lastIndexOf(".") + 1);
628 				Method method = ReflectionUtils.findMethod(serviceClass, methodName, new Class[] { domainClass });
629 				if (method != null) {
630 					ReflectionUtils.invokeMethod(method, service, new Object[] { model });
631 					logger.debug("called " + methodName + " ......");
632 				} else {
633 					service.save(model);
634 				}
635 			} else {
636 				//判断是否存在updateXxxxx方法
637 				String methodName = "update" + domainClass.getName().substring(domainClass.getName().lastIndexOf(".") + 1);
638 				Method method = ReflectionUtils.findMethod(serviceClass, methodName, new Class[] { domainClass });
639 				if (method != null) {
640 					ReflectionUtils.invokeMethod(method, service, new Object[] { model });
641 					logger.debug("called " + methodName + " ......");
642 				} else {
643 					service.update(model);
644 				}
645 			}
646 		}
647 
648 		postSave();
649 	}
650 
651 	protected void initSave() {
652 
653 	}
654 
655 	protected void postSave() {
656 
657 	}
658 
659 	/* (non-Javadoc)
660 	 * @see com.opensymphony.xwork2.Preparable#prepare()
661 	 */
662 	@Override
663 	public final void prepare() throws Exception {
664 	    Class<?> clazz = this.getClass();
665 	    String invokeMethodName = ActionContext.getContext().getActionInvocation().getProxy().getMethod();
666 		logger.debug("prepare {}: {}......", clazz.getName(), invokeMethodName);
667 
668         Map<String, Object> param = request.getParameterMap();
669         boolean getForMetadata = param.containsKey("metadata") && StringUtils.isEmpty(request.getParameter("metadata"));
670         // 获取 metadata 不进行权限判断(此处是为了兼容老版本的代码)
671         if (!getForMetadata) {
672             if (!priorityCheck()) {
673                 throw new NoPrivilegeException("你没有操作权限!");
674             }
675             //根据SID是否存在来判断是否对数据有操作权限
676             if (!StringUtils.isEmpty(SID) && !priorityDataCheck()) {
677                 throw new NoPrivilegeException("你没有当前数据的操作权限!");
678             }
679         }
680 
681         // 为 Action 注入 Spring Bean
682         while (clazz != BaseActionSupport.class) {
683             setAutowiredBeans(clazz.getDeclaredFields(), clazz);
684             clazz = clazz.getSuperclass();
685         }
686         
687         // 往 ActionContext 中存入一些调试信息
688         putDebugInfoIfDevMode();
689         
690         boolean getForData = param.containsKey("data") && StringUtils.isEmpty(request.getParameter("data"));
691         boolean getForComponent = param.containsKey("component") && StringUtils.isEmpty(request.getParameter("component"));
692         // 不是获取元数据(metadata、data、component)时,执行 initXxx() 方法
693         if (!(getForMetadata || getForData || getForComponent)) {
694             invokeInitMethod(invokeMethodName);
695         }
696 	}
697 
698 	//操作权限检查
699 	private boolean priorityCheck() {
700 		List<IOperaPrivilegeCheckService> priCheckServiceList = PrivilegeCheckServiceFactory.getPrivilegeSerivce();
701 		if (null != priCheckServiceList && priCheckServiceList.size() == 0) {
702 		    GboatAppContext.output(JsonResult.createFailure("对不起,当前系统权限验证尚未准备就绪,请稍后再试"));
703 		}
704 		Method method;
705 		try {
706 			//获取要执行的Action的方法
707 			String invokeMethod = ActionContext.getContext().getActionInvocation().getProxy().getMethod();
708 			String privilegeMethod = invokeMethod;
709 			//ajaxDelete与delete的权限相等
710 			if (privilegeMethod.startsWith("ajax")) {
711 				privilegeMethod = String.valueOf(Character.toLowerCase(privilegeMethod.charAt(4))) + privilegeMethod.substring(5);
712 			}
713 			method = this.getClass().getMethod(invokeMethod);
714 			//只有被Operation或者Operations注解注释的方法才需要权限校验
715 			if (ModuleService.isMethodOperationAnnotationed(this.getClass(), method, privilegeMethod)) {
716 				if (null != priCheckServiceList && priCheckServiceList.size() > 0) {
717 					for (IOperaPrivilegeCheckService pcs : priCheckServiceList) {
718 						//为防止数据被覆盖,设置为传入的参数为不可修改
719 						priCheckParams.put("ACTION_NAME", this.getClass().getName());
720 						priCheckParams.put("METHOD_NAME", privilegeMethod);
721 						priCheckParams.put("REQUEST", request);
722 						priCheckParams.put("USER_SESSION", request.getSession().getAttribute("COM.GLODON.SSO.SESSION.KEY"));
723 						//priCheckParams.put("MODEL", getModel());
724 						boolean checkResult = pcs.privilegeCheckServiceByMethodName(Collections.unmodifiableMap(priCheckParams), GBoatClassLoader
725 								.getInstance().getBundle(this.getClass().getPackage().getName()));
726 						logger.debug("check " + this.getClass().getName() + "." + invokeMethod + " in prepare : " + checkResult);
727 						//有一个校验不通过,都算没有权限
728 						if (!checkResult) {
729 							return false;
730 						}
731 					}
732 				}
733 			}
734 
735 		} catch (SecurityException e) {
736 			e.printStackTrace();
737 		} catch (NoSuchMethodException e) {
738 			e.printStackTrace();
739 		}
740 		return true;
741 	}
742 
743 	//数据权限校验
744 	private boolean priorityDataCheck() {
745 		List<IDataPrivilegeCheckService> dataPriCheckServiceList = PrivilegeCheckServiceFactory.getDataPrivelegeService();
746 		Method method;
747 		try {
748 			//获取要执行的Action的方法
749 			String invokeMethod = ActionContext.getContext().getActionInvocation().getProxy().getMethod();
750 			String privilegeMethod = invokeMethod;
751 			if (privilegeMethod.startsWith("ajax")) {
752 				privilegeMethod = String.valueOf(Character.toLowerCase(privilegeMethod.charAt(4))) + privilegeMethod.substring(5);
753 			}
754 			method = this.getClass().getMethod(invokeMethod);
755 			//只有被Operation或者Operations注解注释的方法才需要权限校验
756 			if (ModuleService.isMethodOperationAnnotationed(this.getClass(), method, privilegeMethod)) {
757 				if (null != dataPriCheckServiceList && dataPriCheckServiceList.size() > 0) {
758 					for (IDataPrivilegeCheckService pcs : dataPriCheckServiceList) {
759 						//为防止数据被覆盖,设置为传入的参数为不可修改
760 						priCheckParams.put("ACTION_NAME", this.getClass().getName());
761 						priCheckParams.put("METHOD_NAME", invokeMethod);
762 						priCheckParams.put("REQUEST", request);
763 						priCheckParams.put("USER_SESSION", request.getSession().getAttribute("COM.GLODON.SSO.SESSION.KEY"));
764 						priCheckParams.put("MODEL", getModel());
765 						boolean checkResult = pcs.privilegeCheckServiceByMethodName(Collections.unmodifiableMap(priCheckParams), GBoatClassLoader
766 								.getInstance().getBundle(this.getClass().getPackage().getName()));
767 						;
768 						//有一个校验不通过,都算没有权限
769 						if (!checkResult) {
770 							return false;
771 						}
772 					}
773 				}
774 			}
775 		} catch (SecurityException e) {
776 			e.printStackTrace();
777 		} catch (NoSuchMethodException e) {
778 			e.printStackTrace();
779 		}
780 		return true;
781 	}
782 	
783 	private Bundle getBundle(String bundleName) {
784 		Bundle[] bs = context.getBundles();
785 		for (Bundle b : bs) {
786 			if (b.getSymbolicName().equals(bundleName)) {
787 				return b;
788 			}
789 		}
790 		return null;
791 	}
792 	
793 	/**
794 	 * 对action扩展,Business的注入处理,
795 	 */
796 	protected void setAutowiredBeans(Field[] fields, Class<?> clazz) throws Exception {
797 		Bundle bundle =  FrameworkUtil.getBundle(clazz);
798 		Map<String, String> pomMap = Activator.pomCache.get(bundle.getSymbolicName());
799 		boolean project = MapUtils.getBoolean(pomMap, Activator.HEADER_PROJECT_ENABLED, false);
800 		String bundles =  MapUtils.getString(pomMap, Activator.HEADER_EXTENDER_BUNDLES, "");
801 		
802 		for (Field field : fields) {
803             if (!field.isAccessible()) {
804                 field.setAccessible(true);
805             }
806 
807 		    if((project && field.get(this) != null) || (!project && field.getType().getName().lastIndexOf("Business") == -1 && field.get(this) != null)) // 如果字段已经有值了, 则不再进行注入,直接跳过
808 		        continue;
809 		 
810 		    // project为true是项目bundle
811 			String[] b = bundles.split(",");
812 			if (project) {
813 				if (!setValue(field, bundle)) {
814 					for (int i = 0; StringUtils.isNotEmpty(bundles) && i < b.length; i++) {
815 						Bundle bundleExt = getBundle(b[0]);
816 						if (setValue(field, bundleExt)) 
817 							break;
818 					}
819  				}
820 			}else {
821 				boolean flag = false;
822 				for (int i = 0; StringUtils.isNotEmpty(bundles) && i < b.length; i++) {
823 					Bundle bundleExt = getBundle(b[0]);
824 					if (setValue(field, bundleExt)) {
825 						flag = true;
826 						break;
827 					}
828 				}
829 				if (!flag) {
830 					setValue(field, bundle);
831 				}
832 			}
833 		}
834 	}
835 	
836 	protected boolean setValue(Field field, Bundle bundle) throws Exception {
837 		if (bundle == null) return false;
838 		 Resource resource = field.getAnnotation(Resource.class);
839 		 Object value = null;
840 		 
841 		 try {
842 			if(resource != null) { // @Resource
843 		        String name = resource.name();
844 		        Class<?> requiredType = resource.type();
845 		        SpringContextUtil spring = SpringContextUtil.getInstance();
846 		        if(StringUtils.isNotBlank(name) && !Object.class.equals(requiredType)) {
847 		            // 同时按 bean 的 id 和需要的类型匹配
848 		            value = spring.getBeanOfId(name, bundle, requiredType);
849 		        } else if (StringUtils.isNotBlank(name)) {
850 		            // 按 bean 的 id 匹配
851 		            value = spring.getBeanOfId(name, bundle);
852 		        } else if (!Object.class.equals(requiredType)) {
853 		            // 按需要的类型匹配
854 		            value = spring.getBeanOfType(requiredType, bundle);
855 		        } else {
856 		            // 按字段的类型匹配
857 		            value = spring.getBeanOfType(field.getType(), bundle);
858 		        }
859 		    } else {
860 		        Autowired autowired = field.getAnnotation(Autowired.class);
861 		        if(autowired != null) { // @Autowired
862 		            value = SpringContextUtil.getInstance().getBeanOfType(field.getType(), bundle);
863 		            /*
864 		         if (value == null && autowired.required())
865 		             throw new RuntimeException(this.getClass().getName() + " 的字段 [" + field.getType().getName() + " " + field.getName() + "] 使用了 @Autowired 注解, 并且 required=true, 但没有找到匹配类型的 bean");
866 		         */
867 		        }
868 		    }
869 		} catch (Exception e) {
870 			logger.debug("{} bundle autowired not found object", bundle.getSymbolicName());
871 			return false;
872 		}
873 		    
874 	    if(value != null) {
875          logger.debug("set autowired for {} {} : {}", field.getType().getName(), field.getName(), value);
876 	        field.set(this, value);
877 	        return true;
878 	    }
879 	    return false;
880 	}
881 
882 	protected void setAutowiredBeans(Field[] fields, Bundle bundle) throws Exception {
883 		for (Field field : fields) {
884             if (!field.isAccessible()) {
885                 field.setAccessible(true);
886             }
887 		    if(field.get(this) != null) // 如果字段已经有值了, 则不再进行注入,直接跳过
888 		        continue;
889 		    
890 		    Object value = null;
891 		    Resource resource = field.getAnnotation(Resource.class);
892 		    if(resource != null) { // @Resource
893 		        String name = resource.name();
894 		        Class<?> requiredType = resource.type();
895 		        SpringContextUtil spring = SpringContextUtil.getInstance();
896 		        if(StringUtils.isNotBlank(name) && !Object.class.equals(requiredType)) {
897 		            // 同时按 bean 的 id 和需要的类型匹配
898 		            value = spring.getBeanOfId(name, bundle, requiredType);
899 		        } else if (StringUtils.isNotBlank(name)) {
900 		            // 按 bean 的 id 匹配
901 		            value = spring.getBeanOfId(name, bundle);
902 		        } else if (!Object.class.equals(requiredType)) {
903 		            // 按需要的类型匹配
904 		            value = spring.getBeanOfType(requiredType, bundle);
905 		        } else {
906 		            // 按字段的类型匹配
907 		            value = spring.getBeanOfType(field.getType(), bundle);
908 		        }
909 		    } else {
910 		        Autowired autowired = field.getAnnotation(Autowired.class);
911 		        if(autowired != null) { // @Autowired
912 		            value = SpringContextUtil.getInstance().getBeanOfType(field.getType(), bundle);
913 		            /*
914                     if (value == null && autowired.required())
915                         throw new RuntimeException(this.getClass().getName() + " 的字段 [" + field.getType().getName() + " " + field.getName() + "] 使用了 @Autowired 注解, 并且 required=true, 但没有找到匹配类型的 bean");
916                     */
917 		        }
918 		    }
919 		    
920 		    if(value != null) {
921                 logger.debug("set autowired for {} {} : {}", field.getType().getName(), field.getName(), value);
922 		        field.set(this, value);
923 		    }
924 		}
925 	}
926 
927 	/**
928 	 * @return 当前执行的 Action 所在的 Bundle
929 	 */
930 	protected Bundle getActionBundle() {
931 	    if(actionBundle == null) {
932 	        actionBundle = FrameworkUtil.getBundle(this.getClass());
933 	    }
934 		return actionBundle;
935 	}
936 
937 	/**
938 	 * 请求 Action 时, 在调用真正被请求的方法之前, 先调用该方法的初始化方法(如果存在的话), xxx() 方法对应的初始化方法为 initXxx()。
939 	 * <p>
940 	 * <i>注意: 如果子类重写了本类的 edit(), view(), list(), save() 方法,则在请求这四个方法时,对应的 initXxx() 方法不会被执行。</i>
941 	 * </p>
942 	 * @param invokeMethodName 真正要请求的方法的名称
943 	 */
944 	@SuppressWarnings("rawtypes")
945     protected void invokeInitMethod(String invokeMethodName){
946         String[] ignores = new String[] { "edit", "view", "list", "save" };
947         for (String ignore : ignores) {
948             if (ignore.equals(invokeMethodName)) // 需要忽略的方法直接返回
949                 return;
950         }
951         
952         String initMethodName = ActionUtil.getInitMethodName(invokeMethodName);
953         Method initMethod = null;
954         Object[] initMethodArgs = null;
955         if(ActionUtil.isListPage(invokeMethodName, this)) { // 如果是列表查询的方法,则 initXxx() 方法会有一个 Map 参数
956             initMethod = ReflectionUtils.findMethod(this.getClass(), initMethodName, Map.class);
957             if(initMethod != null) {
958                 initMethodArgs = new Object[] { new LinkedHashMap(request.getParameterMap())  };  
959             }
960         } else { // 非列表查询的方法的 initXxx() 方法没有参数
961             initMethod = ReflectionUtils.findMethod(this.getClass(), initMethodName);
962             if(initMethod != null) {
963                 initMethodArgs = new Object[0];
964             }
965         }
966         
967         // 不存在 initXxx() 方法 或 initXxx() 方法为私有(private)时直接返回
968         if (initMethod == null || Modifier.isPrivate(initMethod.getModifiers()))
969             return;
970         
971         if(!initMethod.isAccessible()) {
972             initMethod.setAccessible(true);
973         }
974         // 调用 initXxx()
975         ReflectionUtils.invokeMethod(initMethod, this, initMethodArgs);
976     }
977 	
978 	/** 
979 	  * 默认转到列表页
980 	  * @see com.opensymphony.xwork2.ActionSupport#execute() 
981 	  * @return 返回forward
982 	  */
983     @Override
984 	public String execute() throws DefaultBusinessNestedException {
985 		return list();
986 	}
987     
988     /**
989      * 转发到指定的 JSP 页面, 通过名称为 go 的参数指定需要跳转的路径,在跳转之前会执行 initXxx() 方法(如果 initXxx() 方法存在的话)
990      * @return
991      */
992     public String forward() {
993         go = StringUtils.trimToEmpty(go).replace('\\', '/');
994         if(go.length() == 0)
995             return null;
996         
997         // 老版本的平台,有的 Action 中只定义了 initXxx(),而没有定义 xxx() 方法,在跳转到对应的页面之前先执行对应的 initXxx() 方法
998         int index = go.lastIndexOf('/');
999         String resultCode = (index < 0 ? go : go.substring(index+1));
1000         if(flatResultLayout) {
1001             String simpleName = StringUtils.removeEnd(this.getClass().getSimpleName(), "Action");
1002             // UserGroupAction --> user-group-
1003             String prefix = (simpleName.charAt(0) + simpleName.substring(1).replaceAll("[A-Z]", nameSeparator + "$0")).toLowerCase() + nameSeparator;
1004             if(resultCode.startsWith(prefix)) {
1005                 resultCode = resultCode.substring(prefix.length());
1006             }
1007         }
1008         
1009         Matcher matcher = Pattern.compile(nameSeparator.replaceAll("[$()*+.\\[?\\^{|]", "\\\\$0")).matcher(resultCode);
1010         String methodName = null;
1011         int start = 0;
1012         while(matcher.find()) {
1013             if(methodName == null) {
1014                 methodName = Character.toLowerCase(resultCode.charAt(0)) + resultCode.substring(1, matcher.start());
1015             } else {
1016                 methodName += WordUtils.capitalize(resultCode.substring(start, matcher.start()));
1017             }
1018             start = matcher.end();
1019         }
1020         methodName += WordUtils.capitalize(resultCode.substring(start));
1021         invokeInitMethod(methodName);
1022         
1023         return go;
1024     }
1025 
1026 	/** 
1027 	  * 根据 metadataType 类型选择相应的处理类
1028 	  */
1029     @Override
1030 	public Object getMetadata(String metadataType, String invokeMethod) {
1031 		MetadataSupportStrategy metadataSupportStrategy;
1032 		if (metadataType.equals("metadata")) {
1033 			metadataSupportStrategy = ObtainMetadata.getInstance();
1034 		} else if (metadataType.equals("data")) {
1035 			metadataSupportStrategy = ObtainData.getInstance();
1036 		} else {
1037 			metadataSupportStrategy = ObtainOthers.getInstance();
1038 		}
1039 		return metadataSupportStrategy.getMetadata(metadataType, invokeMethod, this);
1040 	}
1041 
1042 	/**
1043 	 * 根据数据检查list中操作是否可见,如果有此方面的需求 请覆盖此方法,todo 方法应该定义为抽象
1044 	 * @param data 当前行中数据
1045 	 * @param operationCode 操作code
1046 	 * @return 是否展示操作,展示 返回 true ,否则 返回false
1047 	 */
1048 	public boolean operationCheck(Object data, String operationCode) {
1049 		return true;
1050 	}
1051 
1052 	/* (non-Javadoc)
1053 	 * @see org.apache.struts2.osgi.interceptor.BundleContextAware#setBundleContext(org.osgi.framework.BundleContext)
1054 	 */
1055     @Override
1056 	public void setBundleContext(BundleContext context) {
1057 		this.context = context;
1058 	}
1059 
1060     @Override
1061 	public void setServletResponse(HttpServletResponse response) {
1062 		this.response = response;
1063 	}
1064 
1065 	@Override
1066 	public void setServletRequest(HttpServletRequest request) {
1067 		this.request = request;
1068 	}
1069 
1070     @Override
1071 	public void setServletContext(ServletContext servletContext) {
1072 		osgiHost = (OsgiHost) servletContext.getAttribute(StrutsOsgiListener.OSGI_HOST);
1073 	}
1074 
1075 	/**
1076 	 * 获取视图的扩展路径,返回不为空时视图文件名称为“example-edit-extend.md”
1077 	 * @return
1078 	 */
1079 	protected String getExtendForView() {
1080 		return "";
1081 	}
1082 
1083 	/**
1084 	 * 获取Model对象
1085 	 * @return 返回当前model
1086 	 */
1087 	public Object getModel() {
1088 		if (model == null) {
1089 			try {
1090 				prepareModel();
1091 			} catch (Exception e) {
1092 				logger.error(e.getMessage(), e.getCause());
1093 			}
1094 		}
1095 		return model;
1096 	}
1097 
1098 	public void setModel(Object model) {
1099 		this.model = model;
1100 	}
1101 
1102 	/**
1103 	 * 不符合方法命名规范,暂时这样
1104 	 * 用于绑定的简写,使用"_M"等同于"model"
1105 	 * @return 返回当前model
1106 	 */
1107 	public Object get_M() {
1108 		return getModel();
1109 	}
1110 
1111 	public void set_M(Object _M) {
1112 		this.model = _M;
1113 	}
1114 
1115 	/**
1116 	 * 拼装默认的列表页面URI
1117 	 * 
1118 	 * @return 列表页面URI
1119 	 */
1120 	public String getDefaultListUri() {
1121 		StringBuffer forwardUrl = getUriPrefix();
1122 		forwardUrl.append("!list.do");
1123 
1124 		return forwardUrl.toString();
1125 	}
1126 
1127 	/**
1128 	 * 获得默认的保存页面URI
1129 	 * @return 保存页面URI
1130 	 */
1131 	public String getDefaultSaveUri() {
1132 		StringBuffer forwardUrl = getUriPrefix();
1133 		forwardUrl.append("!save.do");
1134 		return forwardUrl.toString();
1135 	}
1136 
1137 	public String getDefaultAjaxSaveUri() {
1138 		StringBuffer forwardUrl = getUriPrefix();
1139 		forwardUrl.append("!ajaxSave.do");
1140 
1141 		return forwardUrl.toString();
1142 	}
1143 
1144 	/**
1145 	 * 获得默认的编辑页面URI
1146 	 * @return 编辑页面URI
1147 	 */
1148 	public String getDefaultEditUri() {
1149 		StringBuffer forwardUrl = getUriPrefix();
1150 		forwardUrl.append("!edit.do");
1151 
1152 		return forwardUrl.toString();
1153 	}
1154 
1155 	/**
1156 	 * 获得默认的查看页面URI
1157 	 * @return 查看页面URI
1158 	 */
1159 	public String getDefaultViewUri() {
1160 		StringBuffer forwardUrl = getUriPrefix();
1161 		forwardUrl.append("!view.do");
1162 
1163 		return forwardUrl.toString();
1164 	}
1165 
1166 	/**
1167 	 * 获得默认的删除请求URI
1168 	 * @return 删除请求URI
1169 	 */
1170 	public String getDefaultDeleteUri() {
1171 		StringBuffer forwardUrl = getUriPrefix();
1172 		forwardUrl.append("!delete.do");
1173 
1174 		return forwardUrl.toString();
1175 	}
1176 
1177 	public String getDefaultAjaxDeleteUri() {
1178 		StringBuffer forwardUrl = getUriPrefix();
1179 		forwardUrl.append("!ajaxDelete.do");
1180 
1181 		return forwardUrl.toString();
1182 	}
1183 
1184 	/**
1185 	 * 获取uri路径的前缀,如ExampleAction的前缀为example,参照convention的约定规则
1186 	 * @return
1187 	 */
1188 	public StringBuffer getUriPrefix() {
1189 		StringBuffer forwardUrl = new StringBuffer();
1190 		Class<?> clazz = this.getClass();
1191 		String className = clazz.getName().replace(clazz.getPackage().getName() + ".", "");
1192 		className = className.replaceAll("Action$", "");
1193 		Pattern pattern = Pattern.compile("[A-Z][a-z]*");
1194 		Matcher ma = pattern.matcher(className);
1195 		String matched;
1196 		while (ma.find()) {
1197 			matched = ma.group();
1198 			if (forwardUrl.length() > 0) {
1199 				forwardUrl.append("-");
1200 			}
1201 			forwardUrl.append(matched.substring(0, 1).toLowerCase() + matched.substring(1));
1202 		}
1203 		return forwardUrl;
1204 	}
1205 
1206 	/**
1207 	 * 功能:根据接口获取外部bundle中的服务实现
1208 	 * @param interfaceName
1209 	 * @deprecated
1210 	 * @return
1211 	 */
1212 	public Object getBean(String interfaceName) {
1213 		BundleContext bundleContext = context;
1214 		Object service = bundleContext.getService(bundleContext.getServiceReference(interfaceName));
1215 		return service;
1216 	}
1217 
1218 	/**
1219 	 * 功能:根据接口获取外部bundle中的服务实现
1220 	 * @param interfaceName
1221 	 * @author zhangxj-a
1222 	 * @return
1223 	 */
1224 	public <T> T getBean(Class<T> interfaceClass) {
1225 		BundleContext bundleContext = context;
1226 		T service = (T) bundleContext.getService(bundleContext.getServiceReference(interfaceClass.getName()));
1227 		return service;
1228 	}
1229 
1230 	public String getSID() {
1231 		return SID;
1232 	}
1233 
1234 	public void setSID(String sID) {
1235 		SID = sID;
1236 	}
1237 
1238 	public Page<?> getPage() {
1239 		return this.page;
1240 	}
1241 
1242 	public Map<Object,Object> getPriCheckParams() {
1243 		return priCheckParams;
1244 	}
1245 
1246 	public void setPriCheckParams(Map<Object,Object> priCheckParams) {
1247 		this.priCheckParams = priCheckParams;
1248 	}
1249 
1250 	/**
1251 	 * 往 ActionContext 实例中存放一些调试信息
1252 	 */
1253 	protected void putDebugInfoIfDevMode() {
1254 	    ActionContext actionContext = ActionContext.getContext();
1255         actionContext.put(MIN_SUFFIX, (devMode ? "" : ".min"));
1256         
1257 	    DefaultDebugHook debugHook = DefaultDebugHook.getInstance(); 
1258 		String bundleName = getActionBundle().getSymbolicName();
1259 		if (debugHook.isBundleDebugEnabled(bundleName)) {
1260 		    actionContext.put(CURRENT_BUNDLE_RES_LOCATION, debugHook.getSourceFilePath(bundleName, "/"));
1261 		}
1262 	}
1263 
1264 	public final String get_MdId() {
1265 		return _MdId;
1266 	}
1267 
1268 	public final void set_MdId(String mdId) {
1269 		_MdId = mdId;
1270 	}
1271 
1272 	public String getSortField() {
1273         return sortField;
1274     }
1275 
1276     public void setSortField(String sortField) {
1277         this.sortField = sortField;
1278     }
1279 
1280     public String getSortDirection() {
1281         return sortDirection;
1282     }
1283 
1284     public void setSortDirection(String sortDirection) {
1285         this.sortDirection = sortDirection;
1286     }
1287 
1288     public void setGo(String go) {
1289         this.go = go;
1290     }
1291 
1292     @Override
1293 	public void setServices(List services) {
1294 		// TODO Auto-generated method stub
1295 	}
1296 }