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 }