/*
* Copyright 2014 MovingBlocks
*
* 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.terasology.engine.module;
import com.google.common.collect.Sets;
import org.terasology.assets.Asset;
import org.terasology.engine.TerasologyConstants;
import org.terasology.engine.paths.PathManager;
import org.terasology.module.ClasspathModule;
import org.terasology.module.DependencyInfo;
import org.terasology.module.Module;
import org.terasology.module.ModuleEnvironment;
import org.terasology.module.ModuleLoader;
import org.terasology.module.ModuleMetadata;
import org.terasology.module.ModuleMetadataJsonAdapter;
import org.terasology.module.ModulePathScanner;
import org.terasology.module.ModuleRegistry;
import org.terasology.module.TableModuleRegistry;
import org.terasology.module.sandbox.APIScanner;
import org.terasology.module.sandbox.BytecodeInjector;
import org.terasology.module.sandbox.ModuleSecurityManager;
import org.terasology.module.sandbox.ModuleSecurityPolicy;
import org.terasology.module.sandbox.StandardPermissionProviderFactory;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.lang.reflect.ReflectPermission;
import java.net.URISyntaxException;
import java.security.Policy;
import java.util.Collections;
import java.util.Set;
import java.util.stream.Collectors;
public class ModuleManagerImpl implements ModuleManager {
private StandardPermissionProviderFactory permissionProviderFactory = new StandardPermissionProviderFactory();
private ModuleRegistry registry;
private ModuleEnvironment environment;
private ModuleMetadataJsonAdapter metadataReader;
public ModuleManagerImpl() {
metadataReader = new ModuleMetadataJsonAdapter();
for (ModuleExtension ext : StandardModuleExtension.values()) {
metadataReader.registerExtension(ext.getKey(), ext.getValueType());
}
Module engineModule;
try (Reader reader = new InputStreamReader(getClass().getResourceAsStream("/engine-module.txt"), TerasologyConstants.CHARSET)) {
ModuleMetadata metadata = metadataReader.read(reader);
engineModule = ClasspathModule.create(metadata, getClass(), Module.class, Asset.class);
} catch (IOException e) {
throw new RuntimeException("Failed to read engine metadata", e);
} catch (URISyntaxException e) {
throw new RuntimeException("Failed to convert engine library location to path", e);
}
registry = new TableModuleRegistry();
registry.add(engineModule);
ModulePathScanner scanner = new ModulePathScanner(new ModuleLoader(metadataReader));
scanner.getModuleLoader().setModuleInfoPath(TerasologyConstants.MODULE_INFO_FILENAME);
scanner.scan(registry, PathManager.getInstance().getModulePaths());
DependencyInfo engineDep = new DependencyInfo();
engineDep.setId(engineModule.getId());
engineDep.setMinVersion(engineModule.getVersion());
engineDep.setMaxVersion(engineModule.getVersion().getNextPatchVersion());
registry.stream().filter(mod -> mod != engineModule).forEach(mod -> mod.getMetadata().getDependencies().add(engineDep));
setupSandbox();
loadEnvironment(Sets.newHashSet(engineModule), true);
}
private void setupSandbox() {
ExternalApiWhitelist.CLASSES.stream().forEach(clazz ->
permissionProviderFactory.getBasePermissionSet().addAPIClass(clazz));
ExternalApiWhitelist.PACKAGES.stream().forEach(packagee ->
permissionProviderFactory.getBasePermissionSet().addAPIPackage(packagee));
APIScanner apiScanner = new APIScanner(permissionProviderFactory);
registry.stream().filter(Module::isOnClasspath).forEach(apiScanner::scan);
permissionProviderFactory.getBasePermissionSet().grantPermission("com.google.gson", ReflectPermission.class);
permissionProviderFactory.getBasePermissionSet().grantPermission("com.google.gson.internal", ReflectPermission.class);
Policy.setPolicy(new ModuleSecurityPolicy());
System.setSecurityManager(new ModuleSecurityManager());
}
@Override
public ModuleRegistry getRegistry() {
return registry;
}
@Override
public ModuleEnvironment getEnvironment() {
return environment;
}
@Override
public ModuleEnvironment loadEnvironment(Set<Module> modules, boolean asPrimary) {
Set<Module> finalModules = Sets.newLinkedHashSet(modules);
finalModules.addAll(registry.stream().filter(Module::isOnClasspath).collect(Collectors.toList()));
ModuleEnvironment newEnvironment = new ModuleEnvironment(finalModules, permissionProviderFactory, Collections.<BytecodeInjector>emptyList());
if (asPrimary) {
environment = newEnvironment;
}
return newEnvironment;
}
@Override
public ModuleMetadataJsonAdapter getModuleMetadataReader() {
return metadataReader;
}
}