| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| package org.apache.ode.daohib.bpel; |
| |
| import java.sql.Timestamp; |
| import java.text.ParseException; |
| import java.util.ArrayList; |
| import java.util.Date; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| import org.apache.ode.bpel.common.BpelEventFilter; |
| import org.apache.ode.bpel.common.Filter; |
| import org.apache.ode.bpel.common.InstanceFilter; |
| import org.apache.ode.utils.ISO8601DateParser; |
| import org.apache.ode.utils.RelativeDateParser; |
| import org.hibernate.Criteria; |
| import org.hibernate.Query; |
| import org.hibernate.Session; |
| import org.hibernate.criterion.Disjunction; |
| import org.hibernate.criterion.Property; |
| import org.hibernate.criterion.Restrictions; |
| |
| /** |
| * Class used for converting "filter" objects into Hibernate |
| * {@link org.hibernate.Criteria} objects. |
| */ |
| class CriteriaBuilder { |
| static final Logger __log = LoggerFactory.getLogger(CriteriaBuilder.class); |
| |
| /** |
| * Build a HQL query from an instance filter. |
| * @param filter filter |
| */ |
| Query buildHQLQuery(Session session, InstanceFilter filter) { |
| Map<String, Object> parameters = new HashMap<String, Object>(); |
| |
| StringBuffer query = new StringBuffer(); |
| |
| query.append("select pi from HProcessInstance as pi left join fetch pi.fault "); |
| |
| if (filter != null) { |
| // Building each clause |
| ArrayList<String> clauses = new ArrayList<String>(); |
| |
| // iid filter |
| if ( filter.getIidFilter() != null ) { |
| StringBuffer filters = new StringBuffer(); |
| List<String> iids = filter.getIidFilter(); |
| for (int m = 0; m < iids.size(); m++) { |
| filters.append(" pi.id = :iid").append(m); |
| parameters.put("iid" + m, Long.parseLong(iids.get(m))); |
| if (m < iids.size() - 1) filters.append(" or"); |
| } |
| clauses.add(" (" + filters + ")"); |
| } |
| |
| // pid filter |
| if (filter.getPidFilter() != null) { |
| StringBuffer filters = new StringBuffer(); |
| List<String> pids = filter.getPidFilter(); |
| String cmp; |
| if (filter.arePidsNegative()) { |
| cmp = " != "; |
| } else { |
| cmp = " = "; |
| } |
| for (int m = 0; m < pids.size(); m++) { |
| filters.append(" pi.process.processId ").append(cmp).append(" :pid").append(m); |
| parameters.put("pid" + m, pids.get(m)); |
| if (m < pids.size() - 1) filters.append(" or"); |
| } |
| clauses.add(" (" + filters + ")"); |
| } |
| |
| // name filter |
| if (filter.getNameFilter() != null) { |
| clauses.add(" pi.process.typeName like :pname"); |
| parameters.put("pname", filter.getNameFilter().replaceAll("\\*", "%")); |
| } |
| |
| // name space filter |
| if (filter.getNamespaceFilter() != null) { |
| clauses.add(" pi.process.typeNamespace like :pnamespace"); |
| parameters.put("pnamespace", filter.getNamespaceFilter().replaceAll("\\*", "%")); |
| } |
| |
| // started filter |
| if (filter.getStartedDateFilter() != null) { |
| for ( String ds : filter.getStartedDateFilter() ) { |
| // named parameters not needed as date is parsed and is hence not |
| // prone to HQL injections |
| clauses.add(" pi.created " + dateFilter(ds)); |
| } |
| } |
| |
| // last-active filter |
| if (filter.getLastActiveDateFilter() != null) { |
| for ( String ds : filter.getLastActiveDateFilter() ) { |
| // named parameters not needed as date is parsed and is hence not |
| // prone to HQL injections |
| clauses.add(" pi.lastActiveTime " + dateFilter(ds)); |
| } |
| } |
| |
| // status filter |
| if (filter.getStatusFilter() != null) { |
| StringBuffer filters = new StringBuffer(); |
| List<Short> states = filter.convertFilterState(); |
| for (int m = 0; m < states.size(); m++) { |
| filters.append(" pi.state = :pstate").append(m); |
| parameters.put("pstate" + m, states.get(m)); |
| if (m < states.size() - 1) filters.append(" or"); |
| } |
| clauses.add(" (" + filters.toString() + ")"); |
| } |
| |
| // $property filter |
| if (filter.getPropertyValuesFilter() != null) { |
| Map<String,String> props = filter.getPropertyValuesFilter(); |
| // join to correlation sets |
| query.append(" inner join pi.correlationSets as cs"); |
| int i = 0; |
| for (String propKey : props.keySet()) { |
| i++; |
| // join to props for each prop |
| query.append(" inner join cs.properties as csp"+i); |
| // add clause for prop key and value |
| |
| // spaces have to be escaped, might be better handled in InstanceFilter |
| String value = props.get(propKey).replaceAll(" ", " "); |
| if (propKey.startsWith("{")) { |
| String namespace = propKey.substring(1, propKey.lastIndexOf("}")); |
| clauses.add(" csp" + i + ".name = :cspname" + i + |
| " and csp" + i + ".namespace = :cspnamespace" + i + |
| " and csp" + i + ".value = :cspvalue" + i); |
| |
| parameters.put("cspname" + i, propKey.substring(propKey.lastIndexOf("}") + 1, propKey.length())); |
| parameters.put("cspnamespace" + i, namespace); |
| parameters.put("cspvalue" + i, value); |
| } else { |
| clauses.add(" csp" + i + ".name = :cspname" + i + |
| " and csp" + i + ".value = :cspvalue" + i); |
| |
| parameters.put("cspname" + i, propKey); |
| parameters.put("cspvalue" + i, value); |
| } |
| } |
| } |
| |
| // order by |
| StringBuffer orderby = new StringBuffer(""); |
| if (filter.getOrders() != null) { |
| orderby.append(" order by"); |
| List<String> orders = filter.getOrders(); |
| for (int m = 0; m < orders.size(); m++) { |
| String field = orders.get(m); |
| String ord = " asc"; |
| if (field.startsWith("-")) { |
| ord = " desc"; |
| } |
| String fieldName = " pi.id"; |
| if (field.endsWith("name")) { |
| fieldName = " pi.process.typeName"; |
| } |
| if (field.endsWith("namespace")) { |
| fieldName = " pi.process.typeNamespace"; |
| } |
| if ( field.endsWith("version")) { |
| fieldName = " pi.process.version"; |
| } |
| if ( field.endsWith("status")) { |
| fieldName = " pi.state"; |
| } |
| if ( field.endsWith("started")) { |
| fieldName = " pi.created"; |
| } |
| if ( field.endsWith("last-active")) { |
| fieldName = " pi.lastActiveTime"; |
| } |
| orderby.append(fieldName + ord); |
| if (m < orders.size() - 1) orderby.append(", "); |
| } |
| |
| } |
| |
| // Preparing the statement |
| if (clauses.size() > 0) { |
| query.append(" where"); |
| for (int m = 0; m < clauses.size(); m++) { |
| query.append(clauses.get(m)); |
| if (m < clauses.size() - 1) query.append(" and"); |
| } |
| } |
| |
| query.append(orderby); |
| } |
| |
| if (__log.isDebugEnabled()) { |
| __log.debug(query.toString()); |
| } |
| |
| Query q = session.createQuery(query.toString()); |
| |
| for (String p : parameters.keySet()) { |
| q.setParameter(p, parameters.get(p)); |
| } |
| |
| if (filter.getLimit() != 0) { |
| q.setMaxResults(filter.getLimit()); |
| } |
| |
| return q; |
| } |
| |
| private static String dateFilter(String filter) { |
| String date = Filter.getDateWithoutOp(filter); |
| String op = filter.substring(0,filter.indexOf(date)); |
| Date dt = null; |
| try { |
| dt = ISO8601DateParser.parse(date); |
| } catch (ParseException e) { |
| __log.error(e.getMessage(), e); |
| } |
| Timestamp ts = new Timestamp(dt.getTime()); |
| return op + " '" + ts.toString() + "'"; |
| } |
| |
| |
| |
| /** |
| * Build a Hibernate {@link Criteria} from an instance filter. |
| * @param crit target (destination) criteria |
| * @param filter filter |
| */ |
| void buildCriteria(Criteria crit, InstanceFilter filter) { |
| Criteria processCrit = crit.createCriteria("process"); |
| |
| // Filtering on PID |
| List<String> pids = filter.getPidFilter(); |
| if (pids != null && pids.size() > 0) { |
| Disjunction disj = Restrictions.disjunction(); |
| for (String pid: pids) { |
| if( !filter.arePidsNegative() ) { |
| disj.add(Restrictions.eq("processId", pid)); |
| } else { |
| disj.add(Restrictions.ne("processId", pid)); |
| } |
| } |
| processCrit.add(disj); |
| } |
| |
| List<String> iids = filter.getIidFilter(); |
| if (iids != null && iids.size() > 0) { |
| Disjunction disj = Restrictions.disjunction(); |
| for (String iid: iids) { |
| disj.add(Restrictions.eq("id", new Long(iid))); |
| } |
| crit.add(disj); |
| } |
| |
| // Filtering on name and namespace |
| if (filter.getNameFilter() != null) { |
| processCrit.add(Restrictions.like("typeName", filter.getNameFilter().replaceAll("\\*", "%"))); |
| } |
| if (filter.getNamespaceFilter() != null) { |
| processCrit.add(Restrictions.like("typeNamespace", filter.getNamespaceFilter().replaceAll("\\*", "%"))); |
| } |
| |
| // Specific filter for status (using a disjunction between possible statuses) |
| if (filter.getStatusFilter() != null) { |
| List<Short> statuses = filter.convertFilterState(); |
| Disjunction disj = Restrictions.disjunction(); |
| for (short status : statuses) { |
| disj.add(Restrictions.eq("state", status)); |
| } |
| crit.add(disj); |
| } |
| |
| // Specific filter for started and last active dates. |
| if (filter.getStartedDateFilter() != null) { |
| for (String sdf : filter.getStartedDateFilter()) { |
| addFilterOnPrefixedDate(crit, sdf, "created"); |
| } |
| } |
| if (filter.getLastActiveDateFilter() != null) { |
| for (String ladf : filter.getLastActiveDateFilter()) { |
| addFilterOnPrefixedDate(crit, ladf, "lastActiveTime"); |
| } |
| } |
| |
| // Specific filter for correlation properties |
| if (filter.getPropertyValuesFilter() != null) { |
| Criteria propCrit = crit.createCriteria("correlationSets").createCriteria("properties"); |
| for (Map.Entry<String, String> corValue : filter.getPropertyValuesFilter().entrySet()) { |
| String propName = (String)corValue.getKey(); |
| if (propName.startsWith("{")) { |
| String namespace = propName.substring(1, propName.lastIndexOf("}")); |
| propName = propName.substring(propName.lastIndexOf("}") + 1, propName.length()); |
| propCrit.add(Restrictions.eq("name", propName)) |
| .add(Restrictions.eq("namespace", namespace)) |
| .add(Restrictions.eq("value", corValue.getValue())); |
| } else { |
| propCrit.add(Restrictions.eq("name", corValue.getKey())) |
| .add(Restrictions.eq("value", corValue.getValue())); |
| } |
| } |
| } |
| |
| // Ordering |
| if (filter.orders != null) { |
| for (String key : filter.orders) { |
| boolean ascending = true; |
| String orderKey = key; |
| if (key.startsWith("+") || key.startsWith("-")) { |
| orderKey = key.substring(1, key.length()); |
| if (key.startsWith("-")) ascending = false; |
| } |
| |
| if ("name".equals(orderKey)) { |
| if (ascending) processCrit.addOrder(Property.forName("typeName").asc()); |
| else processCrit.addOrder(Property.forName("typeName").desc()); |
| } else if ("namespace".equals(orderKey)) { |
| if (ascending) processCrit.addOrder(Property.forName("typeNamespace").asc()); |
| else processCrit.addOrder(Property.forName("typeNamespace").desc()); |
| } else if ("pid".equals(orderKey)) { |
| if (ascending) processCrit.addOrder(Property.forName("processId").asc()); |
| else processCrit.addOrder(Property.forName("processId").desc()); |
| } else if ("version".equals(orderKey)) { |
| if (ascending) processCrit.addOrder(Property.forName("version").asc()); |
| else processCrit.addOrder(Property.forName("version").desc()); |
| } else if ("status".equals(orderKey)) { |
| if (ascending) crit.addOrder(Property.forName("state").asc()); |
| else crit.addOrder(Property.forName("state").desc()); |
| } else if ("started".equals(orderKey)) { |
| if (ascending) crit.addOrder(Property.forName("created").asc()); |
| else crit.addOrder(Property.forName("created").desc()); |
| } else if ("last-active".equals(orderKey)) { |
| if (ascending) crit.addOrder(Property.forName("lastActiveTime").asc()); |
| else crit.addOrder(Property.forName("lastActiveTime").desc()); |
| } |
| } |
| } |
| |
| if (filter.getLimit() > 0) crit.setMaxResults(filter.getLimit()); |
| } |
| |
| /** |
| * Build criteria for an event filter. |
| * @param crit target criteria |
| * @param efilter event filter |
| */ |
| void buildCriteria(Criteria crit, BpelEventFilter efilter) { |
| if (efilter.getTypeFilter() != null) |
| crit.add(Restrictions.like("type", efilter.getTypeFilter().replace('*','%'))); |
| |
| // Specific filter for started and last active dates. |
| if (efilter.getTimestampFilter() != null) { |
| for (Filter.Restriction<Date> sdf : efilter.getTimestampFilter()) { |
| addFilterOnPrefixedDate(crit, sdf.op, sdf.value, "tstamp"); |
| } |
| } |
| |
| if (efilter.limit > 0) crit.setMaxResults(efilter.limit); |
| } |
| |
| void addScopeFilter(Criteria crit, String scopeId) { |
| crit.add(Restrictions.eq("",scopeId)); |
| } |
| |
| static void addFilterOnPrefixedDate(Criteria crit, String prefixedDate, String dateAttribute) { |
| Date realDate = null; |
| try { |
| realDate = parseDateExpression(getDateWithoutOp(prefixedDate)); |
| } catch (ParseException e) { |
| // Never occurs, the deploy date format is pre-validated by the filter |
| } |
| addFilterOnPrefixedDate(crit,prefixedDate,realDate,dateAttribute); |
| } |
| |
| private static Date parseDateExpression(String date) throws ParseException { |
| if( date.toLowerCase().startsWith("-") && date.length() > 1 ) { |
| return RelativeDateParser.parseRelativeDate(date.substring(1)); |
| } else { |
| return ISO8601DateParser.parse(date); |
| } |
| } |
| |
| static void addFilterOnPrefixedDate(Criteria crit, String op, Date date, String dateAttribute) { |
| if (op.startsWith("=")) { |
| crit.add(Restrictions.eq(dateAttribute, date)); |
| } else if (op.startsWith("<=")) { |
| crit.add(Restrictions.le(dateAttribute, date)); |
| } else if (op.startsWith(">=")) { |
| crit.add(Restrictions.ge(dateAttribute, date)); |
| } else if (op.startsWith("<")) { |
| crit.add(Restrictions.lt(dateAttribute, date)); |
| } else if (op.startsWith(">")) { |
| crit.add(Restrictions.gt(dateAttribute, date)); |
| } |
| } |
| |
| private static String getDateWithoutOp(String ddf) { |
| return Filter.getDateWithoutOp(ddf); |
| } |
| |
| |
| } |