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    }