Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
*/
package io.cloudbeaver.model.rm.local;

import com.google.gson.reflect.TypeToken;
import io.cloudbeaver.DBWConstants;
import io.cloudbeaver.model.app.ServletApplication;
import io.cloudbeaver.service.security.SMUtils;
Expand All @@ -30,9 +31,11 @@
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.DBPDataSourceContainer;
import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry;
import org.jkiss.dbeaver.model.app.DBPProject;
import org.jkiss.dbeaver.model.app.DBPWorkspace;
import org.jkiss.dbeaver.model.auth.SMCredentials;
import org.jkiss.dbeaver.model.auth.SMCredentialsProvider;
import org.jkiss.dbeaver.model.data.json.JSONUtils;
import org.jkiss.dbeaver.model.fs.lock.FileLockController;
import org.jkiss.dbeaver.model.impl.app.BaseProjectImpl;
import org.jkiss.dbeaver.model.impl.auth.SessionContextImpl;
Expand All @@ -56,6 +59,7 @@
import org.jkiss.utils.Pair;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.text.MessageFormat;
import java.time.OffsetDateTime;
Expand All @@ -71,6 +75,7 @@
public class LocalResourceController extends BaseLocalResourceController {

private static final Log log = Log.getLog(LocalResourceController.class);
private static final String PROJECTS_INFO_FILE_NAME = "projects-info.json";

protected final SMCredentialsProvider credentialsProvider;

Expand All @@ -82,6 +87,7 @@ public class LocalResourceController extends BaseLocalResourceController {
protected final List<RMFileOperationHandler> fileHandlers;

private final Map<String, RMLocalProject> projectRegistries = new LinkedHashMap<>();
private final Map<String, RMProjectInfo> sharedProjectsInfo = new LinkedHashMap<>();

public LocalResourceController(
DBPWorkspace workspace,
Expand All @@ -100,6 +106,78 @@ public LocalResourceController(

this.globalProjectName = DBWorkbench.getPlatform().getApplication().getDefaultProjectName();
this.fileHandlers = RMFileOperationHandlersRegistry.getInstance().getFileHandlers();
readProjectInfos(sharedProjectsPath);
}

private void readProjectInfos(@NotNull Path sharedProjectsPath) {
if (!Files.exists(sharedProjectsPath)) {
return;
}

if (Files.exists(sharedProjectsPath.resolve(PROJECTS_INFO_FILE_NAME))) {
try {
log.info("Reading shared project information");
String content = Files.readString(sharedProjectsPath.resolve(PROJECTS_INFO_FILE_NAME), StandardCharsets.UTF_8);
Map<String, RMProjectInfo> loaded = JSONUtils.GSON.fromJson(
content,
TypeToken.getParameterized(Map.class, String.class, RMProjectInfo.class).getType()
);
sharedProjectsInfo.clear();
if (loaded != null) {
sharedProjectsInfo.putAll(loaded);
}
} catch (IOException e) {
log.error("Error reading existing " + PROJECTS_INFO_FILE_NAME, e);
}
return;
}

createProjectInfosFile(sharedProjectsPath);
}

private void createProjectInfosFile(@NotNull Path sharedProjectsPath) {
log.info("Migrating shared project information to the common place");
Map<String, RMProjectInfo> infos = new LinkedHashMap<>();
try (Stream<Path> stream = Files.list(sharedProjectsPath)) {
stream.filter(Files::isDirectory).forEach(projectDir -> {
String name = null;
String description = null;

Path settings = projectDir.resolve(DBPProject.METADATA_FOLDER).resolve(BaseProjectImpl.SETTINGS_STORAGE_FILE);
if (Files.exists(settings) && Files.isRegularFile(settings)) {
try {
String json = Files.readString(settings, StandardCharsets.UTF_8);
Map<String, Object> map = JSONUtils.GSON.fromJson(json, JSONUtils.MAP_TYPE_TOKEN);
name = JSONUtils.getString(map, BaseProjectImpl.PROP_PROJECT_NAME);
description = JSONUtils.getString(map, BaseProjectImpl.PROP_PROJECT_DESCRIPTION);
} catch (IOException e) {
log.warn("Failed to read project settings for " + projectDir + ": " + e.getMessage());
} catch (Exception e) {
log.warn("Failed to parse project settings for " + projectDir + ": " + e.getMessage());
}
}
RMProjectInfo info = new RMProjectInfo();
info.setName(CommonUtils.isEmpty(name) ? projectDir.getFileName().toString() : name);
info.setDescription(description);
String projectId = RMUtils.makeProjectIdFromPath(projectDir, RMProjectType.SHARED);
infos.put(projectId, info);
});
} catch (IOException e) {
log.error("Error listing shared projects path", e);
}
log.info("Migration for project information completed");
sharedProjectsInfo.clear();
sharedProjectsInfo.putAll(infos);
saveProjectsInfo();
}

private void saveProjectsInfo() {
try {
log.info("Saving shared project information");
Files.writeString(sharedProjectsPath.resolve(PROJECTS_INFO_FILE_NAME), JSONUtils.GSON.toJson(sharedProjectsInfo));
} catch (IOException e) {
log.error("Error writing " + PROJECTS_INFO_FILE_NAME, e);
}
}

protected SMController getSecurityController() {
Expand All @@ -121,6 +199,9 @@ protected RMLocalProject getWebProject(String projectId, boolean refresh) throws
RMLocalProject project = projectRegistries.get(projectId);
if (project == null || refresh) {
project = createWebProjectImpl(projectId, new SessionContextImpl(null));
if (RMProjectType.SHARED.equals(project.getProjectType())) {
project.setProjectInfo(getProjectInfoIfNeeded(projectId));
}
projectRegistries.put(projectId, project);
}
return project;
Expand All @@ -135,6 +216,11 @@ protected RMLocalProject createWebProjectImpl(
return new RMLocalProject(workspace, sessionContext, getProjectPath(projectId), WebRMUtils.parseProjectName(projectId).getType());
}

@NotNull
private RMProjectInfo getProjectInfoIfNeeded(@NotNull String projectId) {
return sharedProjectsInfo.computeIfAbsent(projectId, key -> new RMProjectInfo());
}

@NotNull
@Override
public RMProject[] listAccessibleProjects() throws DBException {
Expand Down Expand Up @@ -312,6 +398,8 @@ public RMProject updateProject(@NotNull String projectId, @NotNull RMProjectInfo
throw new DBException("Project '" + projectId + "' is not shared");
}
project.updateProject(projectInfo.getName(), projectInfo.getDescription());
sharedProjectsInfo.put(projectId, projectInfo);
saveProjectsInfo();
return WebRMUtils.createRmProjectFromWebProject(project);
}
}
Expand Down Expand Up @@ -993,6 +1081,9 @@ private RMProject makeProjectFromPath(Path path, Set<RMProjectPermission> permis
.toArray(String[]::new);

RMLocalProject webProject = new RMLocalProject(workspace, new SessionContextImpl(null), path, type);
if (type == RMProjectType.SHARED) {
webProject.setProjectInfo(getProjectInfoIfNeeded(webProject.getId()));
}
return createRmProjectFromWebProject(path, webProject, allProjectPermissions);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@

import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.model.app.DBPWorkspace;
import org.jkiss.dbeaver.model.auth.SMSessionContext;
import org.jkiss.dbeaver.model.rm.RMProjectInfo;
import org.jkiss.dbeaver.model.rm.RMProjectType;
import org.jkiss.dbeaver.model.rm.RMUtils;
import org.jkiss.dbeaver.registry.project.LocalProjectImpl;
Expand All @@ -29,6 +31,8 @@
public class RMLocalProject extends LocalProjectImpl {
@NotNull
private final RMProjectType projectType;
@Nullable
private RMProjectInfo projectInfo;

public RMLocalProject(
@NotNull DBPWorkspace workspace,
Expand All @@ -51,6 +55,39 @@ public String getId() {
return RMUtils.makeProjectIdFromPath(projectPath, projectType);
}

@Nullable
@Override
public String getDescription() {
if (projectInfo == null) {
return null;
}
return projectInfo.getDescription();
}

@NotNull
@Override
public String getName() {
if (projectInfo == null || projectInfo.getName() == null) {
return projectPath.getFileName().toString();
}
return projectInfo.getName();
}

@NotNull
public RMProjectType getProjectType() {
return projectType;
}

@Override
public void updateProject(@Nullable String newName, @Nullable String description) throws DBException {
this.projectInfo = new RMProjectInfo();
projectInfo.setName(newName);
projectInfo.setDescription(description);
}

public void setProjectInfo(@NotNull RMProjectInfo projectInfo) {
this.projectInfo = projectInfo;
}

public boolean canUpdateProjectName() {
return RMProjectType.SHARED.equals(projectType);
Expand Down
Loading