/**
* Copyright 2015 Cloudera Inc.
*
* 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.kitesdk.data.oozie;
import java.net.URI;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.avro.generic.GenericRecord;
import org.apache.hadoop.conf.Configuration;
import org.apache.oozie.ErrorCode;
import org.apache.oozie.XException;
import org.apache.oozie.action.hadoop.LauncherURIHandler;
import org.apache.oozie.dependency.URIHandler;
import org.apache.oozie.dependency.URIHandlerException;
import org.apache.oozie.service.HadoopAccessorException;
import org.apache.oozie.service.Services;
import org.apache.oozie.service.URIHandlerService;
import org.apache.oozie.util.XLog;
import org.kitesdk.data.DatasetException;
import org.kitesdk.data.DatasetNotFoundException;
import org.kitesdk.data.Datasets;
import org.kitesdk.data.Signalable;
import org.kitesdk.data.URIBuilder;
import org.kitesdk.data.View;
import org.kitesdk.data.spi.DefaultConfiguration;
/**
* A Kite URI handler that works with {@link Signalable} views.
*
* To be considered as {@link #exists(URI, Configuration, String) existing} the
* view must have been signaled as ready.
*/
public class KiteURIHandler implements URIHandler {
private static final XLog LOG = XLog.getLog(KiteURIHandler.class);
private Set<String> supportedSchemes;
private List<Class<?>> classesToShip;
private AtomicBoolean confLoaded = new AtomicBoolean(false);
@Override
public void init(final Configuration conf) {
supportedSchemes = new HashSet<String>();
final String[] schemes = conf.getStrings(
URIHandlerService.URI_HANDLER_SUPPORTED_SCHEMES_PREFIX
+ this.getClass().getSimpleName()
+ URIHandlerService.URI_HANDLER_SUPPORTED_SCHEMES_SUFFIX, "view", "dataset");
supportedSchemes.addAll(Arrays.asList(schemes));
classesToShip = new KiteLauncherURIHandler().getClassesForLauncher();
}
@Override
public Set<String> getSupportedSchemes() {
return supportedSchemes;
}
@Override
public Class<? extends LauncherURIHandler> getLauncherURIHandlerClass() {
return KiteLauncherURIHandler.class;
}
@Override
public List<Class<?>> getClassesForLauncher() {
return classesToShip;
}
@Override
public DependencyType getDependencyType(final URI uri)
throws URIHandlerException {
return DependencyType.PULL;
}
@Override
public void registerForNotification(final URI uri, final Configuration conf,
final String user, final String actionID) throws URIHandlerException {
throw new UnsupportedOperationException(
"Notifications are not supported for " + uri);
}
@Override
public boolean unregisterFromNotification(final URI uri, final String actionID) {
throw new UnsupportedOperationException(
"Notifications are not supported for " + uri);
}
@Override
public Context getContext(final URI uri, final Configuration conf,
final String user) throws URIHandlerException {
return null;
}
private synchronized void loadConfiguration() {
if(Services.get() != null) {
KiteConfigurationService kiteService = Services.get().get(KiteConfigurationService.class);
if(kiteService != null) {
Configuration kiteConf = kiteService.getKiteConf();
if(kiteConf != null) {
DefaultConfiguration.set(kiteConf);
} else {
// kite conf was null
LOG.warn("Configuration for Kite not loaded, Kite configuration service config was null.");
}
} else {
// service was null
LOG.warn("Configuration for Kite not loaded, Kite configuration service was not available.");
}
} else {
// services were null
LOG.warn("Configuration for Kite not loaded, oozie services were not available.");
}
}
@SuppressWarnings("rawtypes")
@Override
public boolean exists(final URI uri, final Context context) throws URIHandlerException {
// load the configuration if it hasn't been yet
if(confLoaded.compareAndSet(false, true)) {
loadConfiguration();
}
try {
View<GenericRecord> view = Datasets.load(uri);
if(view instanceof Signalable) {
return ((Signalable)view).isReady();
}
} catch (IllegalArgumentException ex) {
LOG.error("the URI " + uri + " was not a view or dataset");
} catch (DatasetNotFoundException ex) {
LOG.error("the dataset for the URI "+ uri +" did not actually exist");
} catch (DatasetException e) {
throw new HadoopAccessorException(ErrorCode.E0902, e);
}
// didn't meet all the requirements for a URI/view that we want to use with oozie
return false;
}
@Override
public boolean exists(final URI uri, final Configuration conf, final String user)
throws URIHandlerException {
// currently does not handle access limitations between the oozie user and
// a potential dataset owner
return exists(uri, null);
}
@Override
public String getURIWithDoneFlag(final String uri, final String doneFlag)
throws URIHandlerException {
return uri;
}
@Override
public void validate(final String uri) throws URIHandlerException {
try {
URI uriParsed = URI.create(uri);
String scheme = uriParsed.getScheme();
if(!(URIBuilder.VIEW_SCHEME.equals(scheme) || URIBuilder.DATASET_SCHEME.equals(scheme))) {
LOG.error("Unexpected scheme: view, uri was "+ uri);
XException xException = new XException(ErrorCode.E0904, scheme, uri);
throw new URIHandlerException(xException);
}
} catch (IllegalArgumentException iae) {
XException xException = new XException(ErrorCode.E0906, iae);
throw new URIHandlerException(xException);
}
}
@Override
public void destroy() {
}
}