/* * Copyright 2016 Red Hat, Inc. and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.uberfire.preferences.backend; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedHashSet; import java.util.List; import javax.annotation.PostConstruct; import javax.enterprise.context.ApplicationScoped; import javax.inject.Inject; import javax.inject.Named; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.uberfire.annotations.Customizable; import org.uberfire.backend.server.io.object.ObjectStorage; import org.uberfire.io.IOService; import org.uberfire.java.nio.IOException; import org.uberfire.java.nio.file.FileVisitResult; import org.uberfire.java.nio.file.Path; import org.uberfire.java.nio.file.SimpleFileVisitor; import org.uberfire.java.nio.file.attribute.BasicFileAttributes; import org.uberfire.preferences.shared.PreferenceScope; import org.uberfire.preferences.shared.PreferenceScopeFactory; import org.uberfire.preferences.shared.PreferenceScopeTypes; import org.uberfire.preferences.shared.PreferenceStorage; import org.uberfire.preferences.shared.impl.PreferenceScopeResolutionStrategyInfo; import org.uberfire.preferences.shared.impl.PreferenceScopedValue; import org.uberfire.preferences.shared.impl.exception.InvalidPreferenceScopeException; import org.uberfire.rpc.SessionInfo; import static org.uberfire.commons.validation.PortablePreconditions.checkNotNull; import static org.uberfire.java.nio.file.Files.walkFileTree; @ApplicationScoped public class PreferenceStorageImpl implements PreferenceStorage { public static final String FILE_FORMAT = ".preferences"; public static final int FILE_FORMAT_SIZE = FILE_FORMAT.length(); private static final Logger logger = LoggerFactory.getLogger(PreferenceStorageImpl.class); private IOService ioService; private SessionInfo sessionInfo; private PreferenceScopeTypes scopeTypes; private PreferenceScopeFactory scopeFactory; private ObjectStorage objectStorage; protected PreferenceStorageImpl() { } @Inject public PreferenceStorageImpl(@Named("ioStrategy") final IOService ioService, final SessionInfo sessionInfo, @Customizable final PreferenceScopeTypes scopeTypes, final PreferenceScopeFactory scopeFactory, final ObjectStorage objectStorage) { this.ioService = ioService; this.sessionInfo = sessionInfo; this.scopeTypes = scopeTypes; this.scopeFactory = scopeFactory; this.objectStorage = objectStorage; } @PostConstruct public void init() { final String rootPath = "git://preferences"; objectStorage.init(rootPath); } @Override public boolean exists(final PreferenceScope preferenceScope, final String key) { return objectStorage.exists(buildScopedPreferencePath(preferenceScope, key)); } @Override public boolean exists(final PreferenceScopeResolutionStrategyInfo scopeResolutionStrategyInfo, final String key) { for (PreferenceScope preferenceScope : scopeResolutionStrategyInfo.order()) { boolean exists = exists(preferenceScope, key); if (exists) { return true; } } return false; } @Override public <T> T read(final PreferenceScope preferenceScope, final String key) { final String path = buildScopedPreferencePath(preferenceScope, key); return objectStorage.read(path); } @Override public <T> T read(final PreferenceScopeResolutionStrategyInfo scopeResolutionStrategyInfo, final String key) { for (PreferenceScope preferenceScope : scopeResolutionStrategyInfo.order()) { T result = read(preferenceScope, key); if (result != null) { return result; } } return null; } @Override public <T> PreferenceScopedValue<T> readWithScope(final PreferenceScopeResolutionStrategyInfo scopeResolutionStrategyInfo, final String key) { for (PreferenceScope scope : scopeResolutionStrategyInfo.order()) { T result = read(scope, key); if (result != null) { return new PreferenceScopedValue<>(result, scopeFactory.cloneScope(scope)); } } return null; } @Override public void write(final PreferenceScope preferenceScope, final String key, final Object value) { objectStorage.write(buildScopedPreferencePath(preferenceScope, key), value); } @Override public void delete(final PreferenceScope preferenceScope, final String key) { objectStorage.delete(buildScopedPreferencePath(preferenceScope, key)); } @Override public Collection<String> allKeys(final PreferenceScope scope) { Collection<String> keys = new ArrayList<>(); Path path = this.objectStorage.getPath(buildScopePath(scope)); if (ioService.exists(path)) { walkFileTree(checkNotNull("path", path), new SimpleFileVisitor<Path>() { @Override public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException { try { checkNotNull("file", file); final String fileName = file.getFileName().toString(); final int keyEndIndex = fileName.length() - FILE_FORMAT_SIZE; final String fileNameWithoutFormat = fileName.substring(0, keyEndIndex); keys.add(fileNameWithoutFormat); } catch (final Exception ex) { logger.error("An unexpected exception was thrown: ", ex); return FileVisitResult.TERMINATE; } return FileVisitResult.CONTINUE; } }); } return keys; } @Override public Collection<String> allKeys(final List<PreferenceScope> scopes) { Collection<String> keys = new LinkedHashSet<>(); scopes.forEach(scope -> keys.addAll(allKeys(scope))); return keys; } String buildScopePath(final PreferenceScope scope) { if (scope == null) { throw new InvalidPreferenceScopeException("The scope must not be null when building a scope path."); } String path = "/config/"; for (PreferenceScope currentScope = scope; currentScope != null; currentScope = currentScope.childScope()) { path += currentScope.type() + "/" + currentScope.key() + "/"; } return path; } String buildScopedPreferencePath(final PreferenceScope scope, final String key) { return buildScopePath(scope) + key + FILE_FORMAT; } }