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 tinlizard.util.Messages;
025    
026    import java.util.Collection;
027    import java.util.Date;
028    
029    import javax.persistence.Column;
030    import javax.persistence.Entity;
031    import javax.persistence.EntityManager;
032    import javax.persistence.EntityNotFoundException;
033    import javax.persistence.EntityTransaction;
034    import javax.persistence.GeneratedValue;
035    import javax.persistence.GenerationType;
036    import javax.persistence.Id;
037    import javax.persistence.NamedQueries;
038    import javax.persistence.NamedQuery;
039    import javax.persistence.Query;
040    import javax.persistence.Table;
041    import javax.persistence.UniqueConstraint;
042    import javax.persistence.Version;
043    
044    import org.apache.log4j.Logger;
045    import org.apache.maven.scm.ScmException;
046    
047    import org.codehaus.plexus.util.StringUtils;
048    
049    import org.hibernate.search.annotations.DateBridge;
050    import org.hibernate.search.annotations.Field;
051    import org.hibernate.search.annotations.Index;
052    import org.hibernate.search.annotations.Indexed;
053    import org.hibernate.search.annotations.Resolution;
054    import org.hibernate.search.annotations.Store;
055    
056    /**
057     * The main artifact generated by a codeline or collection of codelines.
058     */
059    @Entity(name = "Project")
060    @Table(name = "TL_PROJECT", uniqueConstraints =  {
061        @UniqueConstraint(columnNames =  {
062            "NAME"}
063        )
064    }
065    )
066    @NamedQueries({@NamedQuery(name = QueryNames.PROJECT_BY_NAME,query = "select o from Project o where o.name = ?" + Project.ORDER_BY)
067    })
068    @Indexed
069    public final class Project implements Persistable {
070        static final String ORDER_BY = " order by o.name";
071        private static final Class<Project> CLASS = Project.class;
072        private static final Logger LOG = Logger.getLogger(CLASS);
073        @Id
074        @GeneratedValue(strategy = GenerationType.AUTO)
075        @Column(name = "ID")
076        private Integer id;
077        @Column(name = "NAME", nullable = false)
078        @Field(index = Index.TOKENIZED, store = Store.NO)
079        private String name;
080        @Column(name = "CREATED", nullable = false)
081        @Field(index = Index.UN_TOKENIZED, store = Store.YES)
082        @DateBridge(resolution = Resolution.SECOND)
083        private Date created;
084        @Column(name = "CREATED_BY", nullable = false)
085        @Field(index = Index.TOKENIZED, store = Store.NO)
086        private String createdBy;
087        @Version
088        @Column(name = "LAST_MODIFIED")
089        @Field(index = Index.UN_TOKENIZED, store = Store.YES)
090        @DateBridge(resolution = Resolution.SECOND)
091        private Date lastModified;
092        @Column(name = "LAST_MODIFIED_BY")
093        @Field(index = Index.TOKENIZED, store = Store.NO)
094        private String lastModifiedBy;
095        @Column(name = "DESCRIPTION")
096        @Field(index = Index.TOKENIZED, store = Store.NO)
097        private String description;
098    
099        public Integer getId() {
100            return this.id;
101        }
102    
103        public String getName() {
104            return this.name;
105        }
106    
107        public Date getCreated() {
108            return this.created;
109        }
110    
111        public String getCreatedBy() {
112            return this.createdBy;
113        }
114    
115        public Date getLastModified() {
116            return this.lastModified;
117        }
118    
119        public String getLastModifiedBy() {
120            return this.lastModifiedBy;
121        }
122    
123        public void setId(final Integer id) {
124            this.id = id;
125        }
126    
127        public void setName(final String name) {
128            this.name = name;
129        }
130    
131        public void setCreated(final Date created) {
132            this.created = created;
133        }
134    
135        public void setCreatedBy(final String createdBy) {
136            this.createdBy = createdBy;
137        }
138    
139        public void setLastModified(final Date lastModified) {
140            this.lastModified = lastModified;
141        }
142    
143        public void setLastModifiedBy(final String lastModifiedBy) {
144            this.lastModifiedBy = lastModifiedBy;
145        }
146    
147        public String getDescription() {
148            return this.description;
149        }
150    
151        public void setDescription(final String description) {
152            this.description = description;
153        }
154    
155        public static Collection<Project> findAll() {
156            return JpaDao.getInstance().findAll(CLASS);
157        }
158    
159        public static Project findByName(final String name) {
160            Object[] params = {
161                                  name
162            };
163    
164            return JpaDao.getInstance().findSingleByNamedQuery(CLASS, QueryNames.PROJECT_BY_NAME, params);
165        }
166    
167        public void add() {
168            JpaDao.getInstance().add(this);
169        }
170    
171        public void update() {
172            JpaDao.getInstance().update(this);
173        }
174    
175        public void index() {
176            JpaDao.getInstance().index(this);
177        }
178    
179        public void delete() {
180            TinLizard.getInstance().getScmDao().releaseFiles(getCodelines());
181    
182            EntityManager em = JpaDao.getInstance().getEm();
183            EntityTransaction tx = em.getTransaction();
184            tx.begin();
185    
186            Class<?> clazz = CLASS;
187            Integer id = getId();
188    
189            try {
190                String clazzName;
191                int deleted;
192    
193                //
194                clazzName = Dependency.class.getSimpleName();
195    
196                Query query = em.createQuery("delete from " + clazzName + " o where o.codeline in (select c from " + Codeline.class.getSimpleName() + " c where c.project.id = :id)");
197                query.setParameter("id", id);
198                deleted = query.executeUpdate();
199    
200                if (LOG.isDebugEnabled()) {
201                    LOG.debug("Deleted " + deleted + " of type " + clazzName + ".");
202                }
203    
204                //
205                clazzName = Codeline.class.getSimpleName();
206                query = em.createQuery("delete from " + clazzName + " o where o.project.id = :id");
207                query.setParameter("id", id);
208                deleted = query.executeUpdate();
209    
210                if (LOG.isDebugEnabled()) {
211                    LOG.debug("Deleted " + deleted + " of type " + clazzName + ".");
212                }
213    
214                //
215                clazzName = Project.class.getSimpleName();
216                query = em.createQuery("delete from " + clazzName + " o where o.id = :id");
217                query.setParameter("id", id);
218                deleted = query.executeUpdate();
219    
220                if (LOG.isDebugEnabled()) {
221                    LOG.debug("Deleted " + deleted + " of type " + clazzName + ".");
222                }
223    
224                //
225                tx.commit();
226            } catch (EntityNotFoundException e) {
227                JpaDao.getInstance().handleInfo(tx, ("Object " + clazz + "#" + id + " did not exist"), e);
228            } catch (Exception e) {
229                JpaDao.getInstance().handleError(tx, "Delete Error", e);
230            }
231        }
232    
233        public Codeline findMainLine() {
234            Object[] params = {
235                                  this
236            };
237    
238            return JpaDao.getInstance().findSingleByNamedQuery(Codeline.class, QueryNames.CODELINE_MAIN_LINE, params);
239        }
240    
241        //XXX maybe shouldn't throw error if not found, since that is client/user input.
242        public Codeline getCodeline(final String name) {
243            Object[] params = {
244                                  name,
245                                  this
246            };
247    
248            return JpaDao.getInstance().findSingleByNamedQuery(Codeline.class, QueryNames.CODELINE_BY_NAME, params);
249        }
250    
251        public Collection<Codeline> getCodelines() {
252            Object[] params = {
253                                  this
254            };
255    
256            return JpaDao.getInstance().findByNamedQuery(Codeline.class, QueryNames.CODELINES_ALL_FOR_PROJECT, params);
257        }
258    
259        public Collection<Codeline> getActiveCodelines() {
260            Object[] params = {
261                                  this
262            };
263    
264            return JpaDao.getInstance().findByNamedQuery(Codeline.class, QueryNames.CODELINES_ACTIVE_FOR_PROJECT, params);
265        }
266    
267        public void addCodeline(final Codeline pb) {
268            //XXX add integrity checks that there is only 1 main line.
269            pb.setMainLine(false);
270            pb.setProject(this);
271    
272            if (pb.getView() == null) {
273                View view = View.getDefaultView();
274                pb.setView(view);
275            }
276    
277            pb.add();
278        }
279    
280        public static Project add(final User user, final Project project, final Codeline mainline, final boolean updateFromPom) throws ScmException {
281            if (project == null) {
282                throw new IllegalArgumentException(Messages.error_0106());
283            }
284    
285            String scmUrl = mainline.getScmConnection();
286    
287            if (StringUtils.isBlank(project.getName())) {
288                throw new IllegalArgumentException(Messages.error_0101());
289            }
290    
291            if (StringUtils.isBlank(scmUrl)) {
292                throw new IllegalArgumentException(Messages.error_0102());
293            }
294    
295            if (Project.findByName(project.getName()) != null) {
296                throw new IllegalArgumentException(Messages.error_0103(project.getName()));
297            }
298    
299            String mainLineName = "HEAD"; //XXX
300    
301            Collection<Codeline> existingCodelines = Codeline.findByNameAndScmConnection(mainLineName, scmUrl);
302    
303            if ((existingCodelines != null) && !existingCodelines.isEmpty()) {
304                throw new IllegalArgumentException(Messages.error_0107(mainLineName, scmUrl));
305            }
306    
307            project.add();
308    
309            Codeline pb = mainline;
310            pb.setName(mainLineName);
311            pb.setScmConnection(scmUrl);
312            pb.setOwner(user);
313            pb.setPolicy(Policy.getDefaultPolicy());
314            pb.setMainLine(true);
315            pb.setProject(project);
316            pb.setView(View.getDefaultView());
317            pb.add();
318    
319            if (updateFromPom) {
320                pb.refresh();
321            }
322    
323            return project;
324        }
325    
326        public static Collection<Project> search(final String query) {
327            String[] fields = {
328                                  "name",
329                                  "created",
330                                  "createdBy",
331                                  "lastModified",
332                                  "lastModifiedBy",
333                                  "description"
334            };
335    
336            return JpaDao.getInstance().findByTextSearch(CLASS, query, fields);
337        }
338    
339        public static void indexAll() {
340            JpaDao.getInstance().indexAll(CLASS);
341        }
342    }