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 }