/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.ranger.tagsync.source.file;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ranger.tagsync.model.AbstractTagSource;
import org.apache.ranger.plugin.util.ServiceTags;
import org.apache.ranger.tagsync.model.TagSink;
import org.apache.ranger.tagsync.process.TagSyncConfig;
import org.apache.ranger.tagsync.process.TagSynchronizer;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.Date;
import java.util.Properties;
public class FileTagSource extends AbstractTagSource implements Runnable {
private static final Log LOG = LogFactory.getLog(FileTagSource.class);
private String serviceTagsFileName;
private URL serviceTagsFileURL;
private long lastModifiedTimeInMillis = -1L;
private Gson gsonBuilder;
private long fileModTimeCheckIntervalInMs;
private Thread myThread = null;
public static void main(String[] args) {
FileTagSource fileTagSource = new FileTagSource();
TagSyncConfig config = TagSyncConfig.getInstance();
Properties props = config.getProperties();
if (args.length > 0) {
String tagSourceFileName = args[0];
LOG.info("TagSourceFileName is set to " + args[0]);
props.setProperty(TagSyncConfig.TAGSYNC_FILESOURCE_FILENAME_PROP, tagSourceFileName);
}
TagSynchronizer.printConfigurationProperties(props);
TagSink tagSink = TagSynchronizer.initializeTagSink(props);
if (tagSink != null) {
if (fileTagSource.initialize(props)) {
try {
tagSink.start();
fileTagSource.setTagSink(tagSink);
fileTagSource.synchUp();
} catch (Exception exception) {
LOG.error("ServiceTags upload failed : ", exception);
System.exit(1);
}
} else {
LOG.error("FileTagSource initialized failed, exiting.");
System.exit(1);
}
} else {
LOG.error("TagSink initialialization failed, exiting.");
System.exit(1);
}
}
@Override
public boolean initialize(Properties props) {
if (LOG.isDebugEnabled()) {
LOG.debug("==> FileTagSource.initialize()");
}
Properties properties;
if (props == null || MapUtils.isEmpty(props)) {
LOG.error("No properties specified for FileTagSource initialization");
properties = new Properties();
} else {
properties = props;
}
gsonBuilder = new GsonBuilder().setDateFormat("yyyyMMdd-HH:mm:ss.SSS-Z").setPrettyPrinting().create();
boolean ret = true;
serviceTagsFileName = TagSyncConfig.getTagSourceFileName(properties);
if (StringUtils.isBlank(serviceTagsFileName)) {
ret = false;
LOG.error("value of property 'ranger.tagsync.source.impl.class' is file and no value specified for property 'ranger.tagsync.filesource.filename'!");
}
if (ret) {
fileModTimeCheckIntervalInMs = TagSyncConfig.getTagSourceFileModTimeCheckIntervalInMillis(properties);
if (LOG.isDebugEnabled()) {
LOG.debug("Provided serviceTagsFileName=" + serviceTagsFileName);
LOG.debug("'ranger.tagsync.filesource.modtime.check.interval':" + fileModTimeCheckIntervalInMs + "ms");
}
InputStream serviceTagsFileStream = null;
File f = new File(serviceTagsFileName);
if (f.exists() && f.isFile() && f.canRead()) {
try {
serviceTagsFileStream = new FileInputStream(f);
serviceTagsFileURL = f.toURI().toURL();
} catch (FileNotFoundException exception) {
LOG.error("Error processing input file:" + serviceTagsFileName + " or no privilege for reading file " + serviceTagsFileName, exception);
} catch (MalformedURLException malformedException) {
LOG.error("Error processing input file:" + serviceTagsFileName + " cannot be converted to URL " + serviceTagsFileName, malformedException);
}
} else {
URL fileURL = getClass().getResource(serviceTagsFileName);
if (fileURL == null) {
if (!serviceTagsFileName.startsWith("/")) {
fileURL = getClass().getResource("/" + serviceTagsFileName);
}
}
if (fileURL == null) {
fileURL = ClassLoader.getSystemClassLoader().getResource(serviceTagsFileName);
if (fileURL == null) {
if (!serviceTagsFileName.startsWith("/")) {
fileURL = ClassLoader.getSystemClassLoader().getResource("/" + serviceTagsFileName);
}
}
}
if (fileURL != null) {
try {
serviceTagsFileStream = fileURL.openStream();
serviceTagsFileURL = fileURL;
} catch (Exception exception) {
LOG.error(serviceTagsFileName + " is not a file", exception);
}
} else {
LOG.warn("Error processing input file: URL not found for " + serviceTagsFileName + " or no privilege for reading file " + serviceTagsFileName);
}
}
if (serviceTagsFileStream != null) {
try {
serviceTagsFileStream.close();
} catch (Exception e) {
// Ignore
}
}
}
if (LOG.isDebugEnabled()) {
LOG.debug("<== FileTagSource.initialize(): sourceFileName=" + serviceTagsFileName + ", result=" + ret);
}
return ret;
}
@Override
public boolean start() {
myThread = new Thread(this);
myThread.setDaemon(true);
myThread.start();
return true;
}
@Override
public void stop() {
if (myThread != null && myThread.isAlive()) {
myThread.interrupt();
}
}
@Override
public void run() {
if (LOG.isDebugEnabled()) {
LOG.debug("==> FileTagSource.run()");
}
while (true) {
try {
synchUp();
LOG.debug("Sleeping for [" + fileModTimeCheckIntervalInMs + "] milliSeconds");
Thread.sleep(fileModTimeCheckIntervalInMs);
}
catch (InterruptedException exception) {
LOG.error("Interrupted..: ", exception);
return;
}
}
}
private boolean isChanged() {
if (LOG.isDebugEnabled()) {
LOG.debug("==> FileTagSource.isChanged()");
}
boolean ret = false;
long modificationTime = getModificationTime();
if (modificationTime > lastModifiedTimeInMillis) {
if (LOG.isDebugEnabled()) {
Date modifiedDate = new Date(modificationTime);
Date lastModifiedDate = new Date(lastModifiedTimeInMillis);
LOG.debug("File modified at " + modifiedDate + "last-modified at " + lastModifiedDate);
}
lastModifiedTimeInMillis = modificationTime;
ret = true;
}
if (LOG.isDebugEnabled()) {
LOG.debug("<== FileTagSource.isChanged(): result=" + ret);
}
return ret;
}
public void synchUp() {
if (isChanged()) {
if (LOG.isDebugEnabled()) {
LOG.debug("Begin: update tags from source==>sink");
}
ServiceTags serviceTags = readFromFile();
updateSink(serviceTags);
if (LOG.isDebugEnabled()) {
LOG.debug("End: update tags from source==>sink");
}
} else {
if (LOG.isDebugEnabled()) {
LOG.debug("FileTagSource: no change found for synchronization.");
}
}
}
private ServiceTags readFromFile() {
if (LOG.isDebugEnabled()) {
LOG.debug("==> FileTagSource.readFromFile(): sourceFileName=" + serviceTagsFileName);
}
ServiceTags ret = null;
if (serviceTagsFileURL != null) {
try (
InputStream serviceTagsFileStream = serviceTagsFileURL.openStream();
Reader reader = new InputStreamReader(serviceTagsFileStream, Charset.forName("UTF-8"))
) {
ret = gsonBuilder.fromJson(reader, ServiceTags.class);
} catch (IOException e) {
LOG.warn("Error processing input file: or no privilege for reading file " + serviceTagsFileName, e);
}
} else {
LOG.error("Error reading file: " + serviceTagsFileName);
}
if (LOG.isDebugEnabled()) {
LOG.debug("<== FileTagSource.readFromFile(): sourceFileName=" + serviceTagsFileName);
}
return ret;
}
private long getModificationTime() {
if (LOG.isDebugEnabled()) {
LOG.debug("==> FileTagSource.getLastModificationTime(): sourceFileName=" + serviceTagsFileName);
}
long ret = 0L;
File sourceFile = new File(serviceTagsFileName);
if (sourceFile.exists() && sourceFile.isFile() && sourceFile.canRead()) {
ret = sourceFile.lastModified();
}
if (LOG.isDebugEnabled()) {
LOG.debug("<== FileTagSource.lastModificationTime(): serviceTagsFileName=" + serviceTagsFileName + " result=" + new Date(ret));
}
return ret;
}
}