/* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ package edu.harvard.iq.dataverse.harvest.client; import edu.harvard.iq.dataverse.Dataset; import edu.harvard.iq.dataverse.Dataverse; import java.io.Serializable; import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Index; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.NamedQueries; import javax.persistence.NamedQuery; import javax.persistence.OneToMany; import javax.persistence.OneToOne; import javax.persistence.OrderBy; import javax.persistence.Table; import javax.persistence.Temporal; import javax.persistence.TemporalType; import javax.validation.constraints.Pattern; import javax.validation.constraints.Size; import org.hibernate.validator.constraints.NotBlank; /** * * @author Leonid Andreev */ @Table(indexes = {@Index(columnList="dataverse_id") , @Index(columnList="harvesttype") , @Index(columnList="harveststyle") , @Index(columnList="harvestingurl")}) @Entity @NamedQueries({ @NamedQuery(name = "HarvestingClient.findByNickname", query="SELECT hc FROM HarvestingClient hc WHERE LOWER(hc.name)=:nickName") }) public class HarvestingClient implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public static final String HARVEST_TYPE_OAI="oai"; public static final String HARVEST_TYPE_NESSTAR="nesstar"; /* * Different harvesting "styles". These define how we format and * display meatada harvested from various remote resources. */ public static final String HARVEST_STYLE_DATAVERSE="dataverse"; // pre-4.0 remote Dataverse: public static final String HARVEST_STYLE_VDC="vdc"; public static final String HARVEST_STYLE_ICPSR="icpsr"; public static final String HARVEST_STYLE_NESSTAR="nesstar"; public static final String HARVEST_STYLE_ROPER="roper"; public static final String HARVEST_STYLE_HGL="hgl"; public static final String HARVEST_STYLE_DEFAULT="default"; public static final String HARVEST_STYLE_DESCRIPTION_DATAVERSE="Dataverse v4+"; // pre-4.0 remote Dataverse: public static final String HARVEST_STYLE_DESCRIPTION_VDC="DVN, v2-3"; public static final String HARVEST_STYLE_DESCRIPTION_ICPSR="ICPSR"; public static final String HARVEST_STYLE_DESCRIPTION_NESSTAR="Nesstar archive"; public static final String HARVEST_STYLE_DESCRIPTION_ROPER="Roper Archive"; public static final String HARVEST_STYLE_DESCRIPTION_HGL="HGL"; public static final String HARVEST_STYLE_DESCRIPTION_DEFAULT="Generic OAI resource (DC)"; public static final List<String> HARVEST_STYLE_LIST = Arrays.asList(HARVEST_STYLE_DATAVERSE, HARVEST_STYLE_VDC, HARVEST_STYLE_ICPSR, HARVEST_STYLE_NESSTAR, HARVEST_STYLE_ROPER, HARVEST_STYLE_HGL, HARVEST_STYLE_DEFAULT); public static final List<String> HARVEST_STYLE_DESCRIPTION_LIST = Arrays.asList(HARVEST_STYLE_DESCRIPTION_DATAVERSE, HARVEST_STYLE_DESCRIPTION_VDC, HARVEST_STYLE_DESCRIPTION_ICPSR, HARVEST_STYLE_DESCRIPTION_NESSTAR, HARVEST_STYLE_DESCRIPTION_ROPER, HARVEST_STYLE_DESCRIPTION_HGL, HARVEST_STYLE_DESCRIPTION_DEFAULT); public static final Map<String, String> HARVEST_STYLE_INFOMAP = new LinkedHashMap<String, String>(); static { for (int i=0; i< HARVEST_STYLE_LIST.size(); i++){ HARVEST_STYLE_INFOMAP.put(HARVEST_STYLE_LIST.get(i), HARVEST_STYLE_DESCRIPTION_LIST.get(i)); } } public static final String REMOTE_ARCHIVE_URL_LEVEL_DATAVERSE="dataverse"; public static final String REMOTE_ARCHIVE_URL_LEVEL_DATASET="dataset"; public static final String REMOTE_ARCHIVE_URL_LEVEL_FILE="file"; public static final String SCHEDULE_PERIOD_DAILY="daily"; public static final String SCHEDULE_PERIOD_WEEKLY="weekly"; public HarvestingClient() { this.harvestType = HARVEST_TYPE_OAI; // default harvestType this.harvestStyle = HARVEST_STYLE_DATAVERSE; // default harvestStyle } @ManyToOne @JoinColumn(name="dataverse_id") private Dataverse dataverse; public Dataverse getDataverse() { return this.dataverse; } public void setDataverse(Dataverse dataverse) { this.dataverse = dataverse; } @OneToMany (mappedBy="harvestedFrom", cascade={CascadeType.MERGE, CascadeType.REMOVE}, orphanRemoval=true) private List<Dataset> harvestedDatasets; public List<Dataset> getHarvestedDatasets() { return this.harvestedDatasets; } public void setHarvestedDatasets(List<Dataset> harvestedDatasets) { this.harvestedDatasets = harvestedDatasets; } @NotBlank(message = "Please enter a nickname.") @Column(nullable = false, unique=true) @Size(max = 30, message = "Nickname must be at most 30 characters.") @Pattern.List({@Pattern(regexp = "[a-zA-Z0-9\\_\\-]*", message = "Found an illegal character(s). Valid characters are a-Z, 0-9, '_', and '-'."), @Pattern(regexp=".*\\D.*", message="Nickname should not be a number")}) private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } private String harvestType; public String getHarvestType() { return harvestType; } public void setHarvestType(String harvestType) { this.harvestType = harvestType; } public boolean isOai() { return HARVEST_TYPE_OAI.equals(harvestType); } private String harvestStyle; public String getHarvestStyle() { return harvestStyle; } public void setHarvestStyle(String harvestStyle) { this.harvestStyle = harvestStyle; } private String harvestingUrl; public String getHarvestingUrl() { return this.harvestingUrl; } public void setHarvestingUrl(String harvestingUrl) { this.harvestingUrl = harvestingUrl.trim(); } private String archiveUrl; public String getArchiveUrl() { return this.archiveUrl; } public void setArchiveUrl(String archiveUrl) { this.archiveUrl = archiveUrl; } @Column(columnDefinition="TEXT") private String archiveDescription; public String getArchiveDescription() { return this.archiveDescription; } public void setArchiveDescription(String archiveDescription) { this.archiveDescription = archiveDescription; } private String harvestingSet; public String getHarvestingSet() { return this.harvestingSet; } public void setHarvestingSet(String harvestingSet) { this.harvestingSet = harvestingSet; } private String metadataPrefix; public String getMetadataPrefix() { return metadataPrefix; } public void setMetadataPrefix(String metadataPrefix) { this.metadataPrefix = metadataPrefix; } // TODO: do we need "orphanRemoval=true"? -- L.A. 4.4 // TODO: should it be @OrderBy("startTime")? -- L.A. 4.4 @OneToMany(mappedBy="harvestingClient", cascade={CascadeType.REMOVE, CascadeType.MERGE, CascadeType.PERSIST}) @OrderBy("id") private List<ClientHarvestRun> harvestHistory; List<ClientHarvestRun> getRunHistory() { return harvestHistory; } void setRunHistory(List<ClientHarvestRun> harvestHistory) { this.harvestHistory = harvestHistory; } public String getLastResult() { if (harvestHistory == null || harvestHistory.size() == 0) { return null; } return harvestHistory.get(harvestHistory.size() - 1).getResultLabel(); } public ClientHarvestRun getLastRun() { if (harvestHistory == null || harvestHistory.size() == 0) { return null; } return harvestHistory.get(harvestHistory.size() - 1); } public ClientHarvestRun getLastSuccessfulRun() { if (harvestHistory == null || harvestHistory.size() == 0) { return null; } int i = harvestHistory.size() - 1; while (i > -1) { if (harvestHistory.get(i).isSuccess()) { return harvestHistory.get(i); } i--; } return null; } ClientHarvestRun getLastNonEmptyRun() { if (harvestHistory == null || harvestHistory.size() == 0) { return null; } int i = harvestHistory.size() - 1; while (i > -1) { if (harvestHistory.get(i).isSuccess()) { if (harvestHistory.get(i).getHarvestedDatasetCount().longValue() > 0 || harvestHistory.get(i).getDeletedDatasetCount().longValue() > 0) { return harvestHistory.get(i); } } i--; } return null; } public Date getLastHarvestTime() { ClientHarvestRun lastHarvest = getLastRun(); if ( lastHarvest != null) { return lastHarvest.getStartTime(); } return null; } public Date getLastSuccessfulHarvestTime() { ClientHarvestRun lastSuccessfulHarvest = getLastSuccessfulRun(); if ( lastSuccessfulHarvest != null) { return lastSuccessfulHarvest.getStartTime(); } return null; } public Date getLastNonEmptyHarvestTime() { ClientHarvestRun lastNonEmptyHarvest = getLastNonEmptyRun(); if ( lastNonEmptyHarvest != null) { return lastNonEmptyHarvest.getStartTime(); } return null; } public Long getLastHarvestedDatasetCount() { ClientHarvestRun lastNonEmptyHarvest = getLastNonEmptyRun(); if ( lastNonEmptyHarvest != null) { return lastNonEmptyHarvest.getHarvestedDatasetCount(); } return null; } public Long getLastFailedDatasetCount() { ClientHarvestRun lastNonEmptyHarvest = getLastNonEmptyRun(); if ( lastNonEmptyHarvest != null) { return lastNonEmptyHarvest.getFailedDatasetCount(); } return null; } public Long getLastDeletedDatasetCount() { ClientHarvestRun lastNonEmptyHarvest = getLastNonEmptyRun(); if ( lastNonEmptyHarvest != null) { return lastNonEmptyHarvest.getDeletedDatasetCount(); } return null; } /* move the fields below to the new HarvestingClientRun class: private String harvestResult; public String getResult() { return harvestResult; } public void setResult(String harvestResult) { this.harvestResult = harvestResult; } // "Last Harvest Time" is the last time we *attempted* to harvest // from this remote resource. // It wasn't necessarily a successful attempt! @Temporal(value = TemporalType.TIMESTAMP) private Date lastHarvestTime; public Date getLastHarvestTime() { return lastHarvestTime; } public void setLastHarvestTime(Date lastHarvestTime) { this.lastHarvestTime = lastHarvestTime; } // This is the last "successful harvest" - i.e., the last time we // tried to harvest, and got a response from the remote server. // We may not have necessarily harvested any useful content though; // the result may have been a "no content" or "no changes since the last harvest" // response. @Temporal(value = TemporalType.TIMESTAMP) private Date lastSuccessfulHarvestTime; public Date getLastSuccessfulHarvestTime() { return lastSuccessfulHarvestTime; } public void setLastSuccessfulHarvestTime(Date lastSuccessfulHarvestTime) { this.lastSuccessfulHarvestTime = lastSuccessfulHarvestTime; } // Finally, this is the time stamp from the last "non-empty" harvest. // I.e. the last time we ran a harvest that actually resulted in // some Datasets created, updated or deleted: @Temporal(value = TemporalType.TIMESTAMP) private Date lastNonEmptyHarvestTime; public Date getLastNonEmptyHarvestTime() { return lastNonEmptyHarvestTime; } public void setLastNonEmptyHarvestTime(Date lastNonEmptyHarvestTime) { this.lastNonEmptyHarvestTime = lastNonEmptyHarvestTime; } // And these are the Dataset counts from that last "non-empty" harvest: private Long harvestedDatasetCount; private Long failedDatasetCount; private Long deletedDatasetCount; public Long getLastHarvestedDatasetCount() { return harvestedDatasetCount; } public void setHarvestedDatasetCount(Long harvestedDatasetCount) { this.harvestedDatasetCount = harvestedDatasetCount; } public Long getLastFailedDatasetCount() { return failedDatasetCount; } public void setFailedDatasetCount(Long failedDatasetCount) { this.failedDatasetCount = failedDatasetCount; } public Long getLastDeletedDatasetCount() { return deletedDatasetCount; } public void setDeletedDatasetCount(Long deletedDatasetCount) { this.deletedDatasetCount = deletedDatasetCount; } */ private boolean scheduled; public boolean isScheduled() { return this.scheduled; } public void setScheduled(boolean scheduled) { this.scheduled = scheduled; } private String schedulePeriod; public String getSchedulePeriod() { return schedulePeriod; } public void setSchedulePeriod(String schedulePeriod) { this.schedulePeriod = schedulePeriod; } private Integer scheduleHourOfDay; public Integer getScheduleHourOfDay() { return scheduleHourOfDay; } public void setScheduleHourOfDay(Integer scheduleHourOfDay) { this.scheduleHourOfDay = scheduleHourOfDay; } private Integer scheduleDayOfWeek; public Integer getScheduleDayOfWeek() { return scheduleDayOfWeek; } public void setScheduleDayOfWeek(Integer scheduleDayOfWeek) { this.scheduleDayOfWeek = scheduleDayOfWeek; } public String getScheduleDescription() { Date date = new Date(); Calendar cal = new GregorianCalendar(); cal.setTime(date); SimpleDateFormat weeklyFormat = new SimpleDateFormat(" E h a "); SimpleDateFormat dailyFormat = new SimpleDateFormat(" h a "); String desc = "Not Scheduled"; if (schedulePeriod!=null && schedulePeriod!="") { cal.set(Calendar.HOUR_OF_DAY, scheduleHourOfDay); if (schedulePeriod.equals(this.SCHEDULE_PERIOD_WEEKLY)) { cal.set(Calendar.DAY_OF_WEEK,scheduleDayOfWeek); desc="Weekly, "+weeklyFormat.format(cal.getTime()); } else { desc="Daily, "+dailyFormat.format(cal.getTime()); } } return desc; } private boolean harvestingNow; public boolean isHarvestingNow() { return this.harvestingNow; } public void setHarvestingNow(boolean harvestingNow) { this.harvestingNow = harvestingNow; } private boolean deleted; public boolean isDeleteInProgress() { return this.deleted; } public void setDeleteInProgress(boolean deleteInProgress) { this.deleted = deleteInProgress; } @Override public int hashCode() { int hash = 0; hash += (id != null ? id.hashCode() : 0); return hash; } @Override public boolean equals(Object object) { // TODO: Warning - this method won't work in the case the id fields are not set if (!(object instanceof HarvestingClient)) { return false; } HarvestingClient other = (HarvestingClient) object; if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) { return false; } return true; } @Override public String toString() { return "edu.harvard.iq.dataverse.harvest.client.HarvestingClient[ id=" + id + " ]"; } }