/*******************************************************************************
* Copyright 2011
* Ubiquitous Knowledge Processing (UKP) Lab
* Technische Universität Darmstadt
*
* 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.dkpro.lab.engine.impl;
import static org.dkpro.lab.Util.close;
import static org.dkpro.lab.engine.impl.ImportUtil.extractConstraints;
import static org.dkpro.lab.storage.StorageService.CONTEXT_ID_SCHEME;
import static org.dkpro.lab.storage.StorageService.LATEST_CONTEXT_SCHEME;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Map;
import org.dkpro.lab.conversion.ConversionService;
import org.dkpro.lab.engine.LifeCycleManager;
import org.dkpro.lab.engine.TaskContext;
import org.dkpro.lab.engine.TaskContextFactory;
import org.dkpro.lab.engine.TaskExecutionService;
import org.dkpro.lab.logging.LoggingService;
import org.dkpro.lab.storage.StorageService;
import org.dkpro.lab.storage.StreamReader;
import org.dkpro.lab.storage.StreamWriter;
import org.dkpro.lab.storage.TaskContextNotFoundException;
import org.dkpro.lab.storage.UnresolvedImportException;
import org.dkpro.lab.storage.StorageService.AccessMode;
import org.dkpro.lab.storage.StorageService.StorageKey;
import org.dkpro.lab.task.TaskContextMetadata;
import org.springframework.dao.DataAccessResourceFailureException;
public class DefaultTaskContext
implements TaskContext
{
private final TaskContextFactory owner;
private LoggingService loggingService;
private StorageService storageService;
private ConversionService conversionService;
private LifeCycleManager lifeCycleManager;
private TaskContextMetadata metadata;
private TaskExecutionService executionService;
public DefaultTaskContext(final TaskContextFactory aOwner)
{
owner = aOwner;
metadata = new TaskContextMetadata();
}
@Override
public TaskContextFactory getTaskContextFactory()
{
return owner;
}
@Override
public String getId()
{
return getMetadata().getId();
}
@Override
public void message(String msg)
{
getLoggingService().message(getId(), msg);
}
@Override
public void error(String aMsg)
{
getLoggingService().error(getId(), aMsg, null);
}
@Override
public void error(String aMsg, Throwable aCause)
{
getLoggingService().error(getId(), aMsg, aCause);
}
@Override
public void destroy()
{
owner.destroyContext(this);
}
public void setStorageService(StorageService aStorage)
{
storageService = aStorage;
}
@Override
public StorageService getStorageService()
{
return storageService;
}
public void setLoggingService(LoggingService aLoggingService)
{
loggingService = aLoggingService;
}
@Override
public LoggingService getLoggingService()
{
return loggingService;
}
public void setConversionService(ConversionService aConversionService)
{
conversionService = aConversionService;
}
@Override
public ConversionService getConversionService()
{
return conversionService;
}
public void setLifeCycleManager(LifeCycleManager aLifeCycleManager)
{
lifeCycleManager = aLifeCycleManager;
}
@Override
public LifeCycleManager getLifeCycleManager()
{
return lifeCycleManager;
}
public void setExecutionService(TaskExecutionService aExecutionService)
{
executionService = aExecutionService;
}
@Override
public TaskExecutionService getExecutionService()
{
return executionService;
}
@Override
public void storeBinary(String aPath, StreamWriter aStreamWriter)
{
// Data is always stored to the current context. No need to resolve.
getStorageService().storeBinary(getId(), aPath, aStreamWriter);
}
@Override
public void storeBinary(String aPath, InputStream aStream)
{
// Data is always stored to the current context. No need to resolve.
getStorageService().storeBinary(getId(), aPath, aStream);
}
@Override
public boolean containsKey(String aPath)
{
return getStorageService().containsKey(getId(), aPath)
|| getMetadata().getImports().containsKey(aPath);
}
@Override
public <T extends StreamReader> T retrieveBinary(String aPath, T aReader)
{
StorageKey key = resolve(aPath, AccessMode.READONLY, false);
return getStorageService().retrieveBinary(key.contextId, key.key, aReader);
}
public void setMetadata(TaskContextMetadata aMetadata)
{
metadata = aMetadata;
}
@Override
public TaskContextMetadata getMetadata()
{
return metadata;
}
@Deprecated
@Override
public File getStorageLocation(String aKey, AccessMode aMode)
{
StorageKey key;
StorageService storage = getStorageService();
Map<String, String> imports = getMetadata().getImports();
if (storage.containsKey(getId(), aKey)) {
// If the context contains the key, we do nothing. Locally available data always
// supersedes imported data.
key = new StorageKey(getId(), aKey);
}
else if (imports.containsKey(aKey)) {
URI uri;
try {
uri = new URI(imports.get(aKey));
}
catch (URISyntaxException e) {
throw new DataAccessResourceFailureException("Imported key [" + aKey
+ "] resolves to illegal URL [" + imports.get(aKey) + "]", e);
}
if ("file".equals(uri.getScheme()) && new File(uri).isDirectory()) {
if (aMode == AccessMode.READONLY) {
return new File(uri);
}
else {
// Here we should probably just copy the imported folder into the context
throw new DataAccessResourceFailureException("READWRITE access of imported " +
"folders is not implemented yet.");
}
}
else {
key = resolve(aKey, aMode, true);
}
}
else {
key = resolve(aKey, aMode, true);
}
return getStorageService().getStorageFolder(key.contextId, key.key);
}
@Override
public File getFile(String aKey, AccessMode aMode)
{
StorageKey key;
StorageService storage = getStorageService();
Map<String, String> imports = getMetadata().getImports();
if (storage.containsKey(getId(), aKey)) {
// If the context contains the key, we do nothing. Locally available data always
// supersedes imported data.
key = new StorageKey(getId(), aKey);
}
else if (imports.containsKey(aKey)) {
URI uri;
try {
uri = new URI(imports.get(aKey));
}
catch (URISyntaxException e) {
throw new DataAccessResourceFailureException("Imported key [" + aKey
+ "] resolves to illegal URL [" + imports.get(aKey) + "]", e);
}
if ("file".equals(uri.getScheme()) && new File(uri).isFile()) {
if (aMode == AccessMode.READONLY) {
return new File(uri);
}
else {
// Here we should probably just copy the imported file into the context
throw new DataAccessResourceFailureException("READWRITE access of imported " +
"files is not implemented yet.");
}
}
else {
key = resolve(aKey, aMode, true);
}
}
else {
key = resolve(aKey, aMode, true);
}
File file = getStorageService().locateKey(key.contextId, key.key);
if (file.exists() && !file.isFile()) {
throw new DataAccessResourceFailureException("Key [" + aKey
+ "] resolves to [" + file + "] which is not a file.");
}
return file;
}
@Override
public File getFolder(String aKey, AccessMode aMode)
{
StorageKey key;
StorageService storage = getStorageService();
Map<String, String> imports = getMetadata().getImports();
if (storage.containsKey(getId(), aKey)) {
// If the context contains the key, we do nothing. Locally available data always
// supersedes imported data.
key = new StorageKey(getId(), aKey);
}
else if (imports.containsKey(aKey)) {
URI uri;
try {
uri = new URI(imports.get(aKey));
}
catch (URISyntaxException e) {
throw new DataAccessResourceFailureException("Imported key [" + aKey
+ "] resolves to illegal URL [" + imports.get(aKey) + "]", e);
}
if ("file".equals(uri.getScheme()) && new File(uri).isDirectory()) {
if (aMode == AccessMode.READONLY) {
return new File(uri);
}
else {
// Here we should probably just copy the imported folder into the context
throw new DataAccessResourceFailureException("READWRITE access of imported " +
"folders is not implemented yet.");
}
}
else {
key = resolve(aKey, aMode, true);
}
}
else {
key = resolve(aKey, aMode, true);
}
File folder = getStorageService().locateKey(key.contextId, key.key);
if (!folder.exists()) {
folder.mkdirs();
}
if (!folder.isDirectory()) {
throw new DataAccessResourceFailureException("Key [" + aKey
+ "] resolves to [" + folder + "] which is not a folder.");
}
return folder;
}
@Override
public TaskContextMetadata resolve(URI aUri)
{
StorageService storage = getStorageService();
if (LATEST_CONTEXT_SCHEME.equals(aUri.getScheme())) {
try {
return storage.getLatestContext(aUri.getAuthority(), extractConstraints(aUri));
}
catch (TaskContextNotFoundException e) {
throw new UnresolvedImportException(this, aUri.toString(), e);
}
}
else if (CONTEXT_ID_SCHEME.equals(aUri.getScheme())) {
try {
return storage.getContext(aUri.getAuthority());
}
catch (TaskContextNotFoundException e) {
throw new UnresolvedImportException(this, aUri.toString(), e);
}
}
else {
throw new DataAccessResourceFailureException("Unknown scheme in import ["+aUri+"]");
}
}
public
StorageKey resolve(String aKey, AccessMode aMode, boolean aAllowMissing)
{
StorageService storage = getStorageService();
Map<String, String> imports = getMetadata().getImports();
if (storage.containsKey(getId(), aKey)) {
// If the context contains the key, we do nothing. Locally available data always
// supersedes imported data.
return new StorageKey(getId(), aKey);
}
else if (imports.containsKey(aKey)) {
URI uri;
try {
uri = new URI(imports.get(aKey));
}
catch (URISyntaxException e) {
throw new DataAccessResourceFailureException("Imported key [" + aKey
+ "] resolves to illegal URL [" + imports.get(aKey) + "]", e);
}
// Try resolving by ID or by type/constraints
StorageKey key = null;
if (CONTEXT_ID_SCHEME.equals(uri.getScheme()) || LATEST_CONTEXT_SCHEME.equals(uri.getScheme())) {
TaskContextMetadata meta = resolve(uri);
key = new StorageKey(meta.getId(), uri.getPath());
}
// If the resource is imported from another context and will be modified it has to
// be copied into the current context. The storage may decide though not to copy
// data at this point if it can assure a copy-on-write behavior. E.g. it may copy
// imported storage folders now but imported stream-access (files) keys later.
if (key != null) {
switch (aMode) {
case ADD_ONLY:
case READWRITE:
storage.copy(getId(), aKey, key, aMode);
return new StorageKey(getId(), aKey);
case READONLY:
return key;
}
}
// If this is an external URL, copy it to the current context and then return a location
// in the current context.
InputStream is = null;
try {
is = uri.toURL().openStream();
storage.storeBinary(getId(), aKey, is);
return new StorageKey(getId(), aKey);
}
catch (MalformedURLException e) {
throw new DataAccessResourceFailureException("Imported external key [" + aKey
+ "] resolves to illegal URL [" + uri + "]", e);
}
catch (IOException e) {
throw new DataAccessResourceFailureException(
"Unable to read data for external key [" + aKey + "] from [" + uri + "]", e);
}
finally {
close(is);
}
}
else if (aAllowMissing) {
return new StorageKey(getId(), aKey);
}
throw new DataAccessResourceFailureException("No resource bound to key [" + aKey + "]");
}
}