/*
 * 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.flink.runtime.webmonitor.handlers;

import com.fasterxml.jackson.core.JsonGenerator;
import org.apache.flink.client.program.PackagedProgram;
import org.apache.flink.client.program.ProgramInvocationException;
import org.apache.flink.runtime.instance.ActorGateway;
import org.apache.flink.runtime.webmonitor.RuntimeMonitorHandler;

import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.StringWriter;
import java.util.Map;
import java.util.jar.JarFile;
import java.util.jar.Manifest;

public class JarListHandler implements RequestHandler {

	private final File jarDir;

	public  JarListHandler(File jarDirectory) {
		jarDir = jarDirectory;
	}

	@Override
	public String handleRequest(Map<String, String> pathParams, Map<String, String> queryParams, ActorGateway jobManager) throws Exception {
		try {
			StringWriter writer = new StringWriter();
			JsonGenerator gen = JsonFactory.jacksonFactory.createGenerator(writer);
			
			gen.writeStartObject();
			gen.writeStringField("address", queryParams.get(RuntimeMonitorHandler.WEB_MONITOR_ADDRESS_KEY));
			gen.writeArrayFieldStart("files");

			File[] list = jarDir.listFiles(new FilenameFilter() {
				@Override
				public boolean accept(File dir, String name) {
					return name.endsWith(".jar");
				}
			});
			
			for (File f : list) {
				// separate the uuid and the name parts.
				String id = f.getName();
				
				int startIndex = id.indexOf("_");
				if (startIndex < 0) {
					continue;
				}
				String name = id.substring(startIndex + 1);
				if (name.length() < 5 || !name.endsWith(".jar")) {
					continue;
				}
				
				gen.writeStartObject();
				gen.writeStringField("id", id);
				gen.writeStringField("name", name);
				gen.writeNumberField("uploaded", f.lastModified());
				gen.writeArrayFieldStart("entry");
				
				String[] classes = new String[0];
				try {
					JarFile jar = new JarFile(f);
					Manifest manifest = jar.getManifest();
					String assemblerClass = null;

					if (manifest != null) {
						assemblerClass = manifest.getMainAttributes().getValue(PackagedProgram.MANIFEST_ATTRIBUTE_ASSEMBLER_CLASS);
						if (assemblerClass == null) {
							assemblerClass = manifest.getMainAttributes().getValue(PackagedProgram.MANIFEST_ATTRIBUTE_MAIN_CLASS);
						}
					}
					if (assemblerClass != null) {
						classes = assemblerClass.split(",");
					}
				} catch (IOException ignored) {
					// we simply show no entries here
				}
				
				// show every entry class that can be loaded later on.
				PackagedProgram program;
				for (String clazz : classes) {
					clazz = clazz.trim();
					try {
						program = new PackagedProgram(f, clazz, new String[0]);
						gen.writeStartObject();
						gen.writeStringField("name", clazz);
						String desc = program.getDescription();
						gen.writeStringField("description", desc == null ? "No description provided" : desc);
						gen.writeEndObject();
					} catch (ProgramInvocationException e) {
						//
					}
				}
				gen.writeEndArray();
				gen.writeEndObject();
			}
			gen.writeEndArray();
			gen.writeEndObject();
			gen.close();
			return writer.toString();
		}
		catch (Exception e) {
			throw new RuntimeException("Failed to fetch jar list: " + e.getMessage(), e);
		}
	}
}

@@ -964,75 +964,8 @@
 am;%0A
-import org.apache.flink.client.program.ProgramInvocationException;%0A
 impo
@@ -3207,29 +3207,25 @@
  here%0A%09%09%09%09%7D%0A
-%09%09%09%09
 %0A
+
 %09%09%09%09// show 
@@ -3275,37 +3275,8 @@
 on.%0A
-%09%09%09%09PackagedProgram program;%0A
 %09%09%09%09
@@ -3332,16 +3332,54 @@
 .trim();
+%0A%0A%09%09%09%09%09PackagedProgram program = null;
 %0A%09%09%09%09%09tr
@@ -3444,16 +3444,165 @@
 ng%5B0%5D);%0A
+%09%09%09%09%09%7D catch (Exception ignored) %7B%0A%09%09%09%09%09%09// ignore jar files which throw an error upon creating a PackagedProgram%0A%09%09%09%09%09%7D%0A%09%09%09%09%09if (program != null) %7B%0A
 %09%09%09%09%09%09ge
@@ -3804,16 +3804,16 @@
  desc);%0A
+
 %09%09%09%09%09%09ge
@@ -3836,63 +3836,8 @@
 ();%0A
-%09%09%09%09%09%7D catch (ProgramInvocationException e) %7B%0A%09%09%09%09%09%09//%0A
 %09%09%09%09

fix error handling for jar files with no main method