Java tutorial
/** * Axelor Business Solutions * * Copyright (C) 2005-2016 Axelor (<http://axelor.com>). * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.axelor.meta; import java.io.IOException; import java.io.Reader; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.persistence.Query; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.axelor.common.StringUtils; import com.axelor.db.JPA; import com.axelor.db.Model; import com.axelor.db.QueryBinder; import com.axelor.inject.Beans; import com.axelor.meta.schema.actions.Action; import com.axelor.meta.schema.actions.ActionGroup; import com.axelor.meta.schema.actions.ActionMethod; import com.axelor.rpc.ActionRequest; import com.axelor.rpc.ActionResponse; import com.axelor.rpc.Context; import com.axelor.rpc.Resource; import com.axelor.script.CompositeScriptHelper; import com.axelor.script.ScriptBindings; import com.axelor.script.ScriptHelper; import com.axelor.text.Templates; import com.google.common.base.Function; import com.google.common.base.Preconditions; import com.google.common.base.Strings; import com.google.common.collect.Collections2; import com.google.common.escape.Escaper; import com.google.common.escape.Escapers; import com.google.common.io.CharStreams; import com.google.inject.servlet.RequestScoped; @RequestScoped public class ActionHandler { private final Logger log = LoggerFactory.getLogger(ActionHandler.class); private ActionRequest request; private Context context; private ScriptBindings bindings; private ScriptHelper scriptHelper; private Pattern pattern = Pattern.compile("^(select\\[\\]|select|action|call|eval):\\s*(.*)"); public ActionHandler(ActionRequest request) { final Context context = request.getContext(); this.request = request; this.context = context; this.scriptHelper = new CompositeScriptHelper(this.context); this.bindings = this.scriptHelper.getBindings(); this.bindings.put("__me__", this); } public Context getContext() { return context; } public ActionRequest getRequest() { return request; } /** * Evaluate the given <code>expression</code>. * * @param expression * the expression to evaluate prefixed with action type * followed by a <code>:</code> * @param references * @return * expression result */ public Object evaluate(String expression) { if (StringUtils.isEmpty(expression)) { return null; } String expr = expression.trim(); if (expr.startsWith("#{") && expr.endsWith("}")) { return handleScript(expr); } String kind = null; Matcher matcher = pattern.matcher(expression); if (matcher.matches()) { kind = matcher.group(1); expr = matcher.group(2); } else { return expr; } if ("eval".equals(kind)) { return handleScript(expr); } if ("action".equals(kind)) { return handleAction(expr); } if ("call".equals(kind)) { return handleCall(expr); } if ("select".equals(kind)) { return handleSelectOne(expr); } if ("select[]".equals(kind)) { return handleSelectAll(expr); } return expr; } public Object call(String className, String method) { ActionResponse response = new ActionResponse(); try { final Class<?> klass = Class.forName(className); final Method m = klass.getMethod(method, ActionRequest.class, ActionResponse.class); final Object obj = Beans.get(klass); m.invoke(obj, new Object[] { request, response }); } catch (Exception e) { log.error(e.getMessage(), e); response.setException(e); } return response; } public Object rpc(String className, String methodCall) { Pattern p = Pattern.compile("(\\w+)\\((.*?)\\)"); Matcher m = p.matcher(methodCall); if (!m.matches()) { return null; } try { final Class<?> klass = Class.forName(className); final Object object = Beans.get(klass); return scriptHelper.call(object, methodCall); } catch (Exception e) { throw new IllegalArgumentException(e); } } public String template(Templates engine, Reader template) throws IOException { return engine.fromText(CharStreams.toString(template)).make(bindings).render(); } @SuppressWarnings("all") private Query select(String query, Object... params) { Preconditions.checkArgument(!Strings.isNullOrEmpty(query)); if (!query.toLowerCase().startsWith("select ")) query = "SELECT " + query; Query q = JPA.em().createQuery(query); QueryBinder.of(q).bind(bindings, params); return q; } public Object selectOne(String query, Object... params) { Query q = select(query, params); q.setMaxResults(1); try { return q.getResultList().get(0); } catch (Exception e) { } return null; } public Object selectAll(String query, Object... params) { try { return select(query, params).getResultList(); } catch (Exception e) { } return null; } public Object selectOne(String query) { return selectOne(query, new Object[] {}); } public Object selectAll(String query) { return selectAll(query, new Object[] {}); } @SuppressWarnings("all") public Object search(Class<?> entityClass, String filter, Map params) { filter = makeMethodCall(String.format("__repo__(%s).all().filter", entityClass.getSimpleName()), filter); com.axelor.db.Query q = (com.axelor.db.Query) handleScript(filter); q = q.bind(bindings); q = q.bind(params); return q.fetchOne(); } private static final Escaper STRING_ESCAPER = Escapers.builder().addEscape('"', "\\\"").build(); private String makeMethodCall(String method, String expression) { expression = expression.trim(); // check if expression is parameterized if (!expression.startsWith("(")) { if (!expression.matches("('|\")")) { expression = "\"" + STRING_ESCAPER.escape(expression) + "\""; } expression = "(" + expression + ")"; } return "#{" + method + expression + "}"; } private Object handleSelectOne(String expression) { expression = makeMethodCall("__me__.selectOne", expression); return handleScript(expression); } private Object handleSelectAll(String expression) { expression = makeMethodCall("__me__.selectAll", expression); return handleScript(expression); } private Object handleScript(String expression) { return scriptHelper.eval(expression); } private Object handleAction(String expression) { Action action = MetaStore.getAction(expression); if (action == null) { log.debug("no such action found: {}", expression); return null; } return action.evaluate(this); } private Object handleCall(String expression) { if (Strings.isNullOrEmpty(expression)) return null; String[] parts = expression.split("\\:"); if (parts.length != 2) { log.error("Invalid call expression: ", expression); return null; } ActionMethod action = new ActionMethod(); ActionMethod.Call call = new ActionMethod.Call(); call.setController(parts[0]); call.setMethod(parts[1]); action.setCall(call); return action.evaluate(this); } private static final String KEY_VALUES = "values"; private static final String KEY_ATTRS = "attrs"; private static final String KEY_VALUE = "value"; private Object toCompact(final Object item) { if (item == null) return null; if (item instanceof Collection) { return Collections2.transform((Collection<?>) item, new Function<Object, Object>() { @Override public Object apply(Object input) { return toCompact(input); } }); } if (item instanceof Model) { Model bean = (Model) item; if (bean.getId() != null && JPA.em().contains(bean)) { return Resource.toMapCompact(bean); } } return item; } /** * This method finds m2o values which are managed instances and converts * them to compact maps to avoid unnecessary data transmission and prevents * object graph recreation issues. */ @SuppressWarnings("all") private Object process(Object data) { if (data == null) return data; if (data instanceof Collection) { final List items = new ArrayList<>(); for (Object item : (Collection) data) { items.add(process(item)); } return items; } if (data instanceof Map) { final Map<String, Object> item = new HashMap<>((Map) data); if (item.containsKey(KEY_VALUES) && item.get(KEY_VALUES) instanceof Map) { final Map<String, Object> values = (Map) item.get(KEY_VALUES); for (String key : values.keySet()) { Object value = values.get(key); if (value instanceof Model) { values.put(key, toCompact(value)); } } } if (item.containsKey(KEY_ATTRS) && item.get(KEY_ATTRS) instanceof Map) { final Map<String, Object> values = (Map) item.get(KEY_ATTRS); for (String key : values.keySet()) { final Map<String, Object> attrs = (Map) values.get(key); if (attrs.containsKey(KEY_VALUE)) { attrs.put(KEY_VALUE, toCompact(attrs.get(KEY_VALUE))); } } } return item; } return data; } public ActionResponse execute() { ActionResponse response = new ActionResponse(); String name = request.getAction(); if (name == null) { throw new NullPointerException("no action provided"); } String[] names = name.split(","); ActionGroup action = new ActionGroup(); for (String item : names) { action.addAction(item); } Object data = action.wrap(this); if (data instanceof ActionResponse) { return (ActionResponse) data; } response.setData(process(data)); response.setStatus(ActionResponse.STATUS_SUCCESS); return response; } }