001    /*
002     * ----------------------------------------------------------------------
003     * Copyright (C) 2009 Enrique Lara (k957@68k.org)
004     *
005     * TinLizard is free software; you can redistribute it and/or
006     * modify it under the terms of the GNU Lesser General Public License
007     * as published by the Free Software Foundation; either version 3.0
008     * of the License, or (at your option) any later version.
009     *
010     * TinLizard is distributed in the hope that it will be useful,
011     * but WITHOUT ANY WARRANTY; without even the implied warranty of
012     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
013     * GNU Lesser General Public License for more details.
014     *
015     * You should have received a copy of the GNU Lesser General Public License
016     * along with TinLizard. If not, see http://www.gnu.org/licenses/.
017     * ----------------------------------------------------------------------
018     */
019    package tinlizard.model;
020    
021    import tinlizard.dao.jpa.JpaDao;
022    import tinlizard.dao.jpa.Persistable;
023    
024    import java.util.ArrayList;
025    import java.util.Calendar;
026    import java.util.Collection;
027    import java.util.Date;
028    import java.util.Iterator;
029    
030    import javax.persistence.Column;
031    import javax.persistence.Entity;
032    import javax.persistence.EntityManager;
033    import javax.persistence.EntityNotFoundException;
034    import javax.persistence.EntityTransaction;
035    import javax.persistence.GeneratedValue;
036    import javax.persistence.GenerationType;
037    import javax.persistence.Id;
038    import javax.persistence.JoinColumn;
039    import javax.persistence.ManyToOne;
040    import javax.persistence.NamedQueries;
041    import javax.persistence.NamedQuery;
042    import javax.persistence.Query;
043    import javax.persistence.Table;
044    import javax.persistence.UniqueConstraint;
045    import javax.persistence.Version;
046    
047    import org.apache.log4j.Logger;
048    import org.apache.maven.model.Model;
049    
050    import org.codehaus.plexus.util.StringUtils;
051    
052    import org.hibernate.annotations.Cache;
053    import org.hibernate.annotations.CacheConcurrencyStrategy;
054    import org.hibernate.search.annotations.DateBridge;
055    import org.hibernate.search.annotations.Field;
056    import org.hibernate.search.annotations.Index;
057    import org.hibernate.search.annotations.Indexed;
058    import org.hibernate.search.annotations.Resolution;
059    import org.hibernate.search.annotations.Store;
060    
061    /**
062     * A Codeline is a branch.
063     */
064    @Entity(name = "Codeline")
065    @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE, region = "tinlizard.model.Codeline")
066    @Table(name = "TL_CODELINE", uniqueConstraints =  {
067        @UniqueConstraint(columnNames =  {
068            "NAME", "PROJECT_ID"}
069        )
070        , @UniqueConstraint(columnNames =  {
071            "NAME", "SCM_CONNECTION"}
072        )
073    }
074    )
075    @NamedQueries({@NamedQuery(name = QueryNames.CODELINE_BY_NAME,query = Codeline.SELECT_FROM + Codeline.LEFT_JOINS + " where o.name = ? and p = ?" + Codeline.ORDER_BY)
076        , @NamedQuery(name = QueryNames.CODELINES_MODIFIED_SINCE_DATE,query = Codeline.SELECT_FROM + Codeline.LEFT_JOINS + " where o.lastModified >= ?" + Codeline.ORDER_BY + " DESC")
077        , @NamedQuery(name = QueryNames.CODELINES_BY_NAME_AND_SCM_CONNECTION,query = Codeline.SELECT_FROM + Codeline.LEFT_JOINS + " where o.name = ? and o.scmConnection = ?" + Codeline.ORDER_BY)
078        , @NamedQuery(name = QueryNames.CODELINE_MAIN_LINE,query = Codeline.SELECT_FROM + Codeline.LEFT_JOINS + " where o.mainLine = true and p = ?" + Codeline.ORDER_BY)
079        , @NamedQuery(name = QueryNames.CODELINES_ACTIVE,query = Codeline.SELECT_FROM + Codeline.LEFT_JOINS + " where o.policy.active = true " + Codeline.ORDER_BY)
080        , @NamedQuery(name = QueryNames.CODELINES_ACTIVE_FOR_PROJECT,query = Codeline.SELECT_FROM + Codeline.LEFT_JOINS + " where p = ? and o.policy.active = true " + Codeline.ORDER_BY)
081        , @NamedQuery(name = QueryNames.CODELINES_ACTIVE_FOR_VIEW,query = Codeline.SELECT_FROM + Codeline.LEFT_JOINS + " where c = ? and o.policy.active = true " + Codeline.ORDER_BY)
082        , @NamedQuery(name = QueryNames.CODELINES_ALL_FOR_PROJECT,query = Codeline.SELECT_FROM + Codeline.LEFT_JOINS + " where p = ?" + Codeline.ORDER_BY)
083        , @NamedQuery(name = QueryNames.CODELINES_ALL_FOR_VIEW,query = Codeline.SELECT_FROM + Codeline.LEFT_JOINS + " where c = ?" + Codeline.ORDER_BY)
084        , @NamedQuery(name = QueryNames.CODELINES_ALL_FOR_POLICY,query = Codeline.SELECT_FROM + Codeline.LEFT_JOINS + " where policy = ?" + Codeline.ORDER_BY)
085        , @NamedQuery(name = QueryNames.CODELINES_DEFINING_ARTIFACT,query = Codeline.SELECT_FROM + Codeline.LEFT_JOINS + " where o.groupId = ? and o.artifactId = ? and o.version = ?" + Codeline.ORDER_BY)
086        , @NamedQuery(name = QueryNames.CODELINES_DEFINING_ARTIFACT_ANY_VERSION,query = Codeline.SELECT_FROM + Codeline.LEFT_JOINS + " where o.groupId = ? and o.artifactId = ?" + Codeline.ORDER_BY)
087        , @NamedQuery(name = QueryNames.CODELINES_DEPENDENT_ON_ARTIFACT,query = Codeline.SELECT_FROM + Codeline.LEFT_JOINS + " where o in (select distinct dep.codeline from Dependency dep where dep.groupId = ? and dep.artifactId = ? and dep.version = ?)" + Codeline.ORDER_BY)
088        , @NamedQuery(name = QueryNames.CODELINES_DEPENDENT_ON_ARTIFACT_ANY_VERSION,query = Codeline.SELECT_FROM + Codeline.LEFT_JOINS + " where o in (select distinct dep.codeline from Dependency dep where dep.groupId = ? and dep.artifactId = ?)" + Codeline.ORDER_BY)
089        , @NamedQuery(name = QueryNames.CODELINES_BY_USER,query = Codeline.SELECT_FROM + Codeline.LEFT_JOINS + " where user = ?" + Codeline.ORDER_BY)
090    })
091    @Indexed
092    public final class Codeline implements Persistable {
093        static final String SELECT_FROM = "select o from Codeline o ";
094        static final String LEFT_JOINS = " left join o.owner user " + " left join o.policy policy " + " left join o.view c " + " left join o.project p ";
095        static final String ORDER_BY = " order by o.name";
096        private static final Class<Codeline> CLASS = Codeline.class;
097        private static final Logger LOG = Logger.getLogger(CLASS);
098        @Id
099        @GeneratedValue(strategy = GenerationType.AUTO)
100        @Column(name = "ID")
101        private Integer id;
102        @Column(name = "NAME", nullable = false)
103        @Field(index = Index.TOKENIZED, store = Store.NO)
104        private String name;
105        @Column(name = "CREATED", nullable = false)
106        @Field(index = Index.UN_TOKENIZED, store = Store.YES)
107        @DateBridge(resolution = Resolution.SECOND)
108        private Date created;
109        @Column(name = "CREATED_BY", nullable = false)
110        @Field(index = Index.TOKENIZED, store = Store.NO)
111        private String createdBy;
112        @Version
113        @Column(name = "LAST_MODIFIED")
114        @Field(index = Index.UN_TOKENIZED, store = Store.YES)
115        @DateBridge(resolution = Resolution.SECOND)
116        private Date lastModified;
117        @Column(name = "LAST_MODIFIED_BY")
118        @Field(index = Index.TOKENIZED, store = Store.NO)
119        private String lastModifiedBy;
120        @Column(name = "SCM_CONNECTION")
121        @Field(index = Index.TOKENIZED, store = Store.NO)
122        private String scmConnection;
123        @Column(name = "GROUP_ID")
124        @Field(index = Index.TOKENIZED, store = Store.NO)
125        private String groupId;
126        @Column(name = "ARTIFACT_ID")
127        @Field(index = Index.TOKENIZED, store = Store.NO)
128        private String artifactId;
129        @Column(name = "VERSION")
130        @Field(index = Index.TOKENIZED, store = Store.NO)
131        private String version;
132        @ManyToOne
133        @JoinColumn(name = "OWNER_ID", nullable = false)
134        private User owner;
135        @ManyToOne
136        @JoinColumn(name = "POLICY_ID", nullable = false)
137        private Policy policy;
138        @Column(name = "MAIN_FLAG", nullable = false)
139        private Boolean mainLine = false;
140        @ManyToOne
141        @JoinColumn(name = "VIEW_ID") //, nullable = false)
142    
143        private View view;
144        @ManyToOne
145        @JoinColumn(name = "PROJECT_ID") //, nullable = false)
146    
147        private Project project;
148        @Column(name = "DESCRIPTION")
149        @Field(index = Index.TOKENIZED, store = Store.NO)
150        private String description;
151    
152        public Integer getId() {
153            return this.id;
154        }
155    
156        public String getName() {
157            return this.name;
158        }
159    
160        public Date getCreated() {
161            return this.created;
162        }
163    
164        public String getCreatedBy() {
165            return this.createdBy;
166        }
167    
168        public Date getLastModified() {
169            return this.lastModified;
170        }
171    
172        public String getLastModifiedBy() {
173            return this.lastModifiedBy;
174        }
175    
176        public void setId(final Integer id) {
177            this.id = id;
178        }
179    
180        public void setName(final String name) {
181            this.name = name;
182        }
183    
184        public void setCreated(final Date created) {
185            this.created = created;
186        }
187    
188        public void setCreatedBy(final String createdBy) {
189            this.createdBy = createdBy;
190        }
191    
192        public void setLastModified(final Date lastModified) {
193            this.lastModified = lastModified;
194        }
195    
196        public void setLastModifiedBy(final String lastModifiedBy) {
197            this.lastModifiedBy = lastModifiedBy;
198        }
199    
200        public String getScmConnection() {
201            return this.scmConnection;
202        }
203    
204        public String getGroupId() {
205            return this.groupId;
206        }
207    
208        public String getArtifactId() {
209            return this.artifactId;
210        }
211    
212        public String getVersion() {
213            return this.version;
214        }
215    
216        public View getView() {
217            return this.view;
218        }
219    
220        public Project getProject() {
221            return this.project;
222        }
223    
224        public User getOwner() {
225            return this.owner;
226        }
227    
228        public Policy getPolicy() {
229            return this.policy;
230        }
231    
232        public boolean isMainLine() {
233            return mainLine;
234        }
235    
236        public Boolean getMainLine() {
237            return mainLine;
238        }
239    
240        public String getDescription() {
241            return this.description;
242        }
243    
244        public void setScmConnection(final String scmConnection) {
245            this.scmConnection = scmConnection;
246        }
247    
248        public void setGroupId(final String groupId) {
249            this.groupId = groupId;
250        }
251    
252        public void setArtifactId(final String artifactId) {
253            this.artifactId = artifactId;
254        }
255    
256        public void setVersion(final String version) {
257            this.version = version;
258        }
259    
260        public void setView(final View view) {
261            this.view = view;
262        }
263    
264        public void setProject(final Project project) {
265            this.project = project;
266        }
267    
268        public void setOwner(final User owner) {
269            this.owner = owner;
270        }
271    
272        public void setPolicy(final Policy policy) {
273            this.policy = policy;
274        }
275    
276        public void setMainLine(final Boolean mainLine) {
277            this.mainLine = mainLine;
278        }
279    
280        public void setDescription(final String description) {
281            this.description = description;
282        }
283    
284        public boolean addDependency(final Dependency dep) {
285            dep.setCodeline(this);
286    
287            return true;
288        }
289    
290        public static Collection<Codeline> findAll() {
291            return JpaDao.getInstance().findAll(CLASS);
292        }
293    
294        public static Collection<Codeline> findAllActive() {
295            Object[] params = {  };
296    
297            return JpaDao.getInstance().findByNamedQuery(CLASS, QueryNames.CODELINES_ACTIVE, params);
298        }
299    
300        public static Collection<Codeline> findRecentlyModified(final int daysAgo) {
301            Calendar cal = Calendar.getInstance();
302            cal.add(Calendar.DATE, -daysAgo);
303    
304            Object[] params = {
305                                  cal.getTime()
306            }; //XXX assert daysAgo is positive.
307    
308            return JpaDao.getInstance().findByNamedQuery(CLASS, QueryNames.CODELINES_MODIFIED_SINCE_DATE, params);
309        }
310    
311        public static Collection<Codeline> findByNameAndScmConnection(final String name, final String scmConnection) {
312            Object[] params = {
313                                  name,
314                                  scmConnection
315            };
316    
317            return JpaDao.getInstance().findByNamedQuery(CLASS, QueryNames.CODELINES_BY_NAME_AND_SCM_CONNECTION, params);
318        }
319    
320        public Collection<Codeline> findAllConsumers(final boolean useVersion) {
321            if (useVersion) {
322                Object[] params = {
323                                      getGroupId(),
324                                      getArtifactId(),
325                                      getVersion()
326                };
327    
328                return JpaDao.getInstance().findByNamedQuery(CLASS, QueryNames.CODELINES_DEPENDENT_ON_ARTIFACT, params);
329            } else {
330                Object[] params = {
331                                      getGroupId(),
332                                      getArtifactId()
333                };
334    
335                return JpaDao.getInstance().findByNamedQuery(CLASS, QueryNames.CODELINES_DEPENDENT_ON_ARTIFACT_ANY_VERSION, params);
336            }
337        }
338    
339        public void add() {
340            JpaDao.getInstance().add(this);
341        }
342    
343        public void update() {
344            JpaDao.getInstance().update(this);
345        }
346    
347        public void index() {
348            JpaDao.getInstance().index(this);
349        }
350    
351        public void delete() {
352            TinLizard.getInstance().getScmDao().releaseFiles(this);
353    
354            EntityManager em = JpaDao.getInstance().getEm();
355            EntityTransaction tx = em.getTransaction();
356            tx.begin();
357    
358            Class<?> clazz = CLASS;
359            Integer id = getId();
360    
361            try {
362                String clazzName;
363                int deleted;
364    
365                //
366                clazzName = Dependency.class.getSimpleName();
367    
368                Query query = em.createQuery("delete from " + clazzName + " o where o.codeline.id = :id");
369                query.setParameter("id", id);
370                deleted = query.executeUpdate();
371    
372                if (LOG.isDebugEnabled()) {
373                    LOG.debug("Deleted " + deleted + " of type " + clazzName + ".");
374                }
375    
376                //
377                clazzName = Codeline.class.getSimpleName();
378                query = em.createQuery("delete from " + clazzName + " o where o.id = :id");
379                query.setParameter("id", id);
380                deleted = query.executeUpdate();
381    
382                if (LOG.isDebugEnabled()) {
383                    LOG.debug("Deleted " + deleted + " of type " + clazzName + ".");
384                }
385    
386                //
387                em.refresh(project);
388    
389                tx.commit();
390            } catch (EntityNotFoundException e) {
391                JpaDao.getInstance().handleInfo(tx, ("Object " + clazz + "#" + id + " did not exist"), e);
392            } catch (Exception e) {
393                JpaDao.getInstance().handleError(tx, "Delete Error", e);
394            }
395        }
396    
397        public void deleteAllDependencies() {
398            EntityManager em = JpaDao.getInstance().getEm();
399            EntityTransaction tx = em.getTransaction();
400            tx.begin();
401    
402            Integer id = getId();
403    
404            try {
405                String clazzName = Dependency.class.getSimpleName();
406    
407                Query query = em.createQuery("delete from " + clazzName + " o where o.codeline.id = :id");
408                query.setParameter("id", id);
409    
410                int deleted = query.executeUpdate();
411    
412                if (LOG.isDebugEnabled()) {
413                    LOG.debug("Deleted " + deleted + " of type " + clazzName + ".");
414                }
415    
416                tx.commit();
417            } catch (Exception e) {
418                JpaDao.getInstance().handleError(tx, "Delete Error", e);
419            }
420        }
421    
422        public Collection<Dependency> findAllDependencies() {
423            Object[] params = {
424                                  this
425            };
426    
427            return JpaDao.getInstance().findByNamedQuery(Dependency.class, QueryNames.DEPENDENCIES_BY_CODELINE, params);
428        }
429    
430        public void refresh() {
431            TinLizard.getInstance().getScmDao().update(this);
432            updateFromPom();
433        }
434    
435        @SuppressWarnings("unchecked")
436        private void updateFromPom() {
437            Model pom = TinLizard.getInstance().getScmDao().getPOM(this); //XXX what about getEffectivePom(pb); ?
438    
439            if (StringUtils.isNotBlank(pom.getGroupId())) {
440                setGroupId(pom.getGroupId());
441            } else {
442                setGroupId(pom.getParent().getGroupId());
443            }
444    
445            setArtifactId(pom.getArtifactId());
446            setVersion(pom.getVersion());
447    
448            deleteAllDependencies();
449    
450            Collection<org.apache.maven.model.Dependency> dependencies = pom.getDependencies();
451    
452            for (org.apache.maven.model.Dependency dependency : dependencies) {
453                Dependency dep = new Dependency();
454                dep.setGroupId(dependency.getGroupId());
455                dep.setArtifactId(dependency.getArtifactId());
456                dep.setVersion(dependency.getVersion());
457                dep.setClassifier(dependency.getClassifier());
458                addDependency(dep);
459                dep.add();
460            }
461    
462            update();
463        }
464    
465        public Model getPOM() {
466            return TinLizard.getInstance().getScmDao().getPOM(this);
467        }
468    
469        @SuppressWarnings("unchecked")
470        public static Collection<String> findAllGroupIds() {
471            EntityManager em = JpaDao.getInstance().getEm();
472            EntityTransaction tx = em.getTransaction();
473            tx.begin();
474    
475            Collection<String> groupIds = null;
476    
477            try {
478                groupIds = new ArrayList<String>();
479    
480                Collection results = em.createQuery("select distinct o.groupId from Codeline o").getResultList();
481    
482                for (Iterator it = results.iterator(); it.hasNext();) {
483                    Object obj = it.next();
484                    groupIds.add(obj.toString());
485                }
486    
487                tx.commit();
488            } catch (Exception e) {
489                JpaDao.getInstance().handleError(tx, "Delete Error", e);
490            }
491    
492            return groupIds;
493        }
494    
495        @SuppressWarnings("unchecked")
496        public static Collection<String> findAllArtifactIds(final String groupId) {
497            EntityManager em = JpaDao.getInstance().getEm();
498            EntityTransaction tx = em.getTransaction();
499            tx.begin();
500    
501            Collection<String> artifactIds = null;
502    
503            try {
504                artifactIds = new ArrayList<String>();
505    
506                Collection results = em.createQuery("select distinct o.artifactId from Codeline o where groupId = ?").setParameter(1, groupId).getResultList();
507    
508                for (Iterator it = results.iterator(); it.hasNext();) {
509                    Object obj = it.next();
510                    artifactIds.add(obj.toString());
511                }
512    
513                tx.commit();
514            } catch (Exception e) {
515                JpaDao.getInstance().handleError(tx, "Delete Error", e);
516            }
517    
518            return artifactIds;
519        }
520    
521        public static Collection<Codeline> search(final String query) {
522            String[] fields = {
523                                  "name",
524                                  "created",
525                                  "createdBy",
526                                  "lastModified",
527                                  "lastModifiedBy",
528                                  "scmConnection",
529                                  "groupId",
530                                  "artifactId",
531                                  "version"
532            };
533    
534            return JpaDao.getInstance().findByTextSearch(CLASS, query, fields);
535        }
536    
537        public static void indexAll() {
538            JpaDao.getInstance().indexAll(CLASS);
539        }
540    }