001 /** 002 * 003 * Licensed to the Apache Software Foundation (ASF) under one or more 004 * contributor license agreements. See the NOTICE file distributed with 005 * this work for additional information regarding copyright ownership. 006 * The ASF licenses this file to You under the Apache License, Version 2.0 007 * (the "License"); you may not use this file except in compliance with 008 * the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 package org.apache.camel.component.jpa; 019 020 import java.lang.reflect.Method; 021 import java.util.List; 022 import javax.persistence.EntityManager; 023 import javax.persistence.LockModeType; 024 import javax.persistence.PersistenceException; 025 import javax.persistence.Query; 026 027 import org.apache.camel.Exchange; 028 import org.apache.camel.Processor; 029 import org.apache.camel.impl.ScheduledPollConsumer; 030 import org.apache.camel.util.ObjectHelper; 031 import org.apache.commons.logging.Log; 032 import org.apache.commons.logging.LogFactory; 033 import org.springframework.orm.jpa.JpaCallback; 034 035 /** 036 * @version $Revision: 541335 $ 037 */ 038 public class JpaConsumer extends ScheduledPollConsumer<Exchange> { 039 private static final transient Log log = LogFactory.getLog(JpaConsumer.class); 040 private final JpaEndpoint endpoint; 041 private final TransactionStrategy template; 042 private QueryFactory queryFactory; 043 private DeleteHandler<Object> deleteHandler; 044 private String query; 045 private String namedQuery; 046 private String nativeQuery; 047 048 public JpaConsumer(JpaEndpoint endpoint, Processor processor) { 049 super(endpoint, processor); 050 this.endpoint = endpoint; 051 this.template = endpoint.createTransactionStrategy(); 052 } 053 054 protected void poll() throws Exception { 055 template.execute(new JpaCallback() { 056 public Object doInJpa(EntityManager entityManager) throws PersistenceException { 057 Query query = getQueryFactory().createQuery(entityManager); 058 configureParameters(query); 059 List results = query.getResultList(); 060 for (Object result : results) { 061 if (log.isDebugEnabled()) { 062 log.debug("Processing new entity: " + result); 063 } 064 065 if (lockEntity(result, entityManager)) { 066 // lets turn the result into an exchange and fire it into the processor 067 Exchange exchange = createExchange(result); 068 try { 069 getProcessor().process(exchange); 070 } catch (Exception e) { 071 throw new PersistenceException(e); 072 } 073 getDeleteHandler().deleteObject(entityManager, result); 074 } 075 } 076 entityManager.flush(); 077 return null; 078 } 079 }); 080 } 081 082 // Properties 083 //------------------------------------------------------------------------- 084 public JpaEndpoint getEndpoint() { 085 return endpoint; 086 } 087 088 public QueryFactory getQueryFactory() { 089 if (queryFactory == null) { 090 queryFactory = createQueryFactory(); 091 if (queryFactory == null) { 092 throw new IllegalArgumentException("No queryType property configured on this consumer, nor an entityType configured on the endpoint so cannot consume"); 093 } 094 } 095 return queryFactory; 096 } 097 098 public void setQueryFactory(QueryFactory queryFactory) { 099 this.queryFactory = queryFactory; 100 } 101 102 public DeleteHandler getDeleteHandler() { 103 if (deleteHandler == null) { 104 deleteHandler = createDeleteHandler(); 105 } 106 return deleteHandler; 107 } 108 109 public void setDeleteHandler(DeleteHandler deleteHandler) { 110 this.deleteHandler = deleteHandler; 111 } 112 113 public String getNamedQuery() { 114 return namedQuery; 115 } 116 117 public void setNamedQuery(String namedQuery) { 118 this.namedQuery = namedQuery; 119 } 120 121 public String getNativeQuery() { 122 return nativeQuery; 123 } 124 125 public void setNativeQuery(String nativeQuery) { 126 this.nativeQuery = nativeQuery; 127 } 128 129 public String getQuery() { 130 return query; 131 } 132 133 public void setQuery(String query) { 134 this.query = query; 135 } 136 137 // Implementation methods 138 //------------------------------------------------------------------------- 139 140 /** 141 * A strategy method to lock an object with an exclusive lock so that it can be processed 142 * 143 * @param entity the entity to be locked 144 * @param entityManager 145 * @return true if the entity was locked 146 */ 147 protected boolean lockEntity(Object entity, EntityManager entityManager) { 148 try { 149 if (log.isDebugEnabled()) { 150 log.debug("Acquiring exclusive lock on entity: " + entity); 151 } 152 entityManager.lock(entity, LockModeType.WRITE); 153 return true; 154 } 155 catch (Exception e) { 156 if (log.isDebugEnabled()) { 157 log.debug("Failed to achieve lock on entity: " + entity + ". Reason: " + e, e); 158 } 159 return false; 160 } 161 } 162 163 protected QueryFactory createQueryFactory() { 164 if (query != null) { 165 return QueryBuilder.query(query); 166 } 167 else if (namedQuery != null) { 168 return QueryBuilder.namedQuery(namedQuery); 169 } 170 else if (nativeQuery != null) { 171 return QueryBuilder.nativeQuery(nativeQuery); 172 } 173 else { 174 Class<?> entityType = endpoint.getEntityType(); 175 if (entityType == null) { 176 return null; 177 } 178 else { 179 return QueryBuilder.query("select x from " + entityType.getName() + " x"); 180 } 181 } 182 } 183 184 protected DeleteHandler<Object> createDeleteHandler() { 185 // TODO auto-discover an annotation in the entity bean to indicate the process completed method call? 186 Class<?> entityType = getEndpoint().getEntityType(); 187 if (entityType != null) { 188 List<Method> methods = ObjectHelper.findMethodsWithAnnotation(entityType, Consumed.class); 189 if (methods.size() > 1) { 190 throw new IllegalArgumentException("Only one method can be annotated with the @Consumed annotation but found: " + methods); 191 } 192 else if (methods.size() == 1) { 193 final Method method = methods.get(0); 194 195 return new DeleteHandler<Object>() { 196 public void deleteObject(EntityManager entityManager, Object entityBean) { 197 ObjectHelper.invokeMethod(method, entityBean); 198 } 199 }; 200 } 201 } 202 return new DeleteHandler<Object>() { 203 public void deleteObject(EntityManager entityManager, Object entityBean) { 204 entityManager.remove(entityBean); 205 } 206 }; 207 } 208 209 protected void configureParameters(Query query) { 210 int maxResults = endpoint.getMaximumResults(); 211 if (maxResults > 0) { 212 query.setMaxResults(maxResults); 213 } 214 } 215 216 protected Exchange createExchange(Object result) { 217 Exchange exchange = endpoint.createExchange(); 218 exchange.getIn().setBody(result); 219 return exchange; 220 } 221 }