/******************************************************************************* * Copyright (c) 2012-2017 Codenvy, S.A. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Codenvy, S.A. - initial API and implementation *******************************************************************************/ package org.eclipse.che.api.workspace.server.model.impl; import org.eclipse.che.account.shared.model.Account; import org.eclipse.che.account.spi.AccountImpl; import org.eclipse.che.api.core.model.workspace.Workspace; import org.eclipse.che.api.core.model.workspace.WorkspaceConfig; import org.eclipse.che.api.core.model.workspace.WorkspaceRuntime; import org.eclipse.che.api.core.model.workspace.WorkspaceStatus; import org.eclipse.che.api.machine.server.model.impl.SnapshotImpl; import org.eclipse.che.commons.lang.NameGenerator; import org.eclipse.persistence.descriptors.DescriptorEvent; import org.eclipse.persistence.descriptors.DescriptorEventAdapter; import javax.persistence.CascadeType; import javax.persistence.CollectionTable; import javax.persistence.Column; import javax.persistence.ElementCollection; import javax.persistence.Entity; import javax.persistence.EntityListeners; import javax.persistence.FetchType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.MapKeyColumn; import javax.persistence.NamedQueries; import javax.persistence.NamedQuery; import javax.persistence.OneToMany; import javax.persistence.OneToOne; import javax.persistence.PrePersist; import javax.persistence.PreUpdate; import javax.persistence.Table; import javax.persistence.Transient; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; /** * Data object for {@link Workspace}. * * @author Yevhenii Voevodin */ @Entity(name = "Workspace") @Table(name = "workspace") @NamedQueries( { @NamedQuery(name = "Workspace.getByNamespace", query = "SELECT w FROM Workspace w WHERE w.account.name = :namespace"), @NamedQuery(name = "Workspace.getByName", query = "SELECT w FROM Workspace w WHERE w.account.name = :namespace AND w.name = :name"), @NamedQuery(name = "Workspace.getAll", query = "SELECT w FROM Workspace w"), @NamedQuery(name = "Workspace.getByTemporary", query = "SELECT w FROM Workspace w WHERE w.isTemporary = :temporary") } ) @EntityListeners(WorkspaceImpl.SyncNameOnUpdateAndPersistEventListener.class) public class WorkspaceImpl implements Workspace { public static WorkspaceImplBuilder builder() { return new WorkspaceImplBuilder(); } @Id @Column(name = "id") private String id; /** * The original workspace name is workspace.config.name * this attribute is stored for unique constraint with account id. * See {@link #syncName()}. */ @Column(name = "name") private String name; @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true) @JoinColumn(name = "config_id") private WorkspaceConfigImpl config; @ElementCollection(fetch = FetchType.EAGER) @CollectionTable(name = "workspace_attributes", joinColumns = @JoinColumn(name = "workspace_id")) @MapKeyColumn(name = "attributes_key") @Column(name = "attributes") private Map<String, String> attributes; @Column(name = "istemporary") private boolean isTemporary; @ManyToOne @JoinColumn(name = "accountid", nullable = false) private AccountImpl account; // This mapping is for explicit constraint between // snapshots and workspace, it's impossible to do so on snapshot side // as workspace and machine are different modules and cyclic reference will appear. @OneToMany(fetch = FetchType.LAZY) @JoinColumn(name = "workspaceId", insertable = false, updatable = false) private List<SnapshotImpl> snapshots; @Transient private WorkspaceStatus status; @Transient private WorkspaceRuntimeImpl runtime; public WorkspaceImpl() {} public WorkspaceImpl(String id, Account account, WorkspaceConfig config) { this(id, account, config, null, null, false, null); } public WorkspaceImpl(String id, Account account, WorkspaceConfig config, WorkspaceRuntime runtime, Map<String, String> attributes, boolean isTemporary, WorkspaceStatus status) { this.id = id; if (account != null) { this.account = new AccountImpl(account); } if (config != null) { this.config = new WorkspaceConfigImpl(config); } if (runtime != null) { this.runtime = new WorkspaceRuntimeImpl(runtime); } if (attributes != null) { this.attributes = new HashMap<>(attributes); } this.isTemporary = isTemporary; this.status = status; } public WorkspaceImpl(Workspace workspace, Account account) { this(workspace.getId(), account, workspace.getConfig(), workspace.getRuntime(), workspace.getAttributes(), workspace.isTemporary(), workspace.getStatus()); } public WorkspaceImpl(WorkspaceImpl workspace) { this(workspace, workspace.account); } @Override public String getId() { return id; } public void setId(String id) { this.id = id; } @Override public String getNamespace() { if (account != null) { return account.getName(); } return null; } public void setAccount(AccountImpl account) { this.account = account; } public AccountImpl getAccount() { return account; } @Override public WorkspaceConfigImpl getConfig() { return config; } public void setConfig(WorkspaceConfigImpl config) { this.config = config; } @Override public Map<String, String> getAttributes() { if (attributes == null) { attributes = new HashMap<>(); } return attributes; } public void setAttributes(Map<String, String> attributes) { this.attributes = attributes; } @Override public boolean isTemporary() { return isTemporary; } public void setTemporary(boolean temporary) { isTemporary = temporary; } @Override public WorkspaceStatus getStatus() { return status; } public void setStatus(WorkspaceStatus status) { this.status = status; } @Override public WorkspaceRuntimeImpl getRuntime() { return runtime; } public void setRuntime(WorkspaceRuntimeImpl runtime) { this.runtime = runtime; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (!(obj instanceof WorkspaceImpl)) return false; final WorkspaceImpl other = (WorkspaceImpl)obj; return Objects.equals(id, other.id) && Objects.equals(getNamespace(), other.getNamespace()) && Objects.equals(status, other.status) && isTemporary == other.isTemporary && getAttributes().equals(other.getAttributes()) && Objects.equals(config, other.config) && Objects.equals(runtime, other.runtime); } @Override public int hashCode() { int hash = 7; hash = 31 * hash + Objects.hashCode(id); hash = 31 * hash + Objects.hashCode(getNamespace()); hash = 31 * hash + Objects.hashCode(status); hash = 31 * hash + Objects.hashCode(config); hash = 31 * hash + getAttributes().hashCode(); hash = 31 * hash + Boolean.hashCode(isTemporary); hash = 31 * hash + Objects.hashCode(runtime); return hash; } @Override public String toString() { return "WorkspaceImpl{" + "id='" + id + '\'' + ", namespace='" + getNamespace() + '\'' + ", name='" + name + '\'' + ", config=" + config + ", isTemporary=" + isTemporary + ", status=" + status + ", attributes=" + attributes + ", runtime=" + runtime + '}'; } /** Syncs {@link #name} with config name. */ private void syncName() { name = config == null ? null : config.getName(); } /** * {@link PreUpdate} and {@link PrePersist} methods are not called when * the configuration is updated and workspace object is not, while listener * methods are always called, even if workspace instance is not changed. */ public static class SyncNameOnUpdateAndPersistEventListener extends DescriptorEventAdapter { @Override public void preUpdate(DescriptorEvent event) { ((WorkspaceImpl)event.getObject()).syncName(); } @Override public void prePersist(DescriptorEvent event) { ((WorkspaceImpl)event.getObject()).syncName(); } @Override public void preUpdateWithChanges(DescriptorEvent event) { ((WorkspaceImpl)event.getObject()).syncName(); } } /** * Helps to build complex {@link WorkspaceImpl workspace} instance. * * @see WorkspaceImpl#builder() */ public static class WorkspaceImplBuilder { private String id; private Account account; private boolean isTemporary; private WorkspaceStatus status; private WorkspaceConfig config; private WorkspaceRuntime runtime; private Map<String, String> attributes; private WorkspaceImplBuilder() {} public WorkspaceImpl build() { return new WorkspaceImpl(id, account, config, runtime, attributes, isTemporary, status); } public WorkspaceImplBuilder generateId() { id = NameGenerator.generate("workspace", 16); return this; } public WorkspaceImplBuilder setConfig(WorkspaceConfig workspaceConfig) { this.config = workspaceConfig; return this; } public WorkspaceImplBuilder setId(String id) { this.id = id; return this; } public WorkspaceImplBuilder setAccount(Account account) { this.account = account; return this; } public WorkspaceImplBuilder setTemporary(boolean isTemporary) { this.isTemporary = isTemporary; return this; } public WorkspaceImplBuilder setStatus(WorkspaceStatus status) { this.status = status; return this; } public WorkspaceImplBuilder setAttributes(Map<String, String> attributes) { this.attributes = attributes; return this; } public WorkspaceImplBuilder setRuntime(WorkspaceRuntime runtime) { this.runtime = runtime; return this; } } }