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 }