/******************************************************************************* * Copyright (c) 2011, 2012, 2013, 2014 Red Hat, Inc. * All rights reserved. * This program is made available under the terms of the * Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Red Hat, Inc. - initial API and implementation * ******************************************************************************/ package org.kie.eclipse.server; import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.Writer; import java.lang.reflect.InvocationTargetException; import java.net.HttpURLConnection; import java.net.URL; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicReference; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.jface.operation.IRunnableWithProgress; import org.eclipse.ui.IWorkbench; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.progress.IProgressService; import org.eclipse.wst.server.core.IServer; import org.eclipse.wst.server.core.ServerPort; import org.kie.eclipse.Activator; import org.kie.eclipse.IKieConstants; import com.eclipsesource.json.JsonObject; /** * */ public abstract class KieServiceDelegate implements IKieServiceDelegate, IKieConstants { protected IKieResourceHandler handler; protected IServer server; // TODO: fetch from Preferences protected static List<String> kieApplicationNames = new ArrayList<String>(); static { kieApplicationNames.add("kie-wb"); kieApplicationNames.add("kie-drools-wb"); kieApplicationNames.add("kie-jbpm-wb"); kieApplicationNames.add("business-central"); kieApplicationNames.add("drools-console"); kieApplicationNames.add("jbpm-console"); kieApplicationNames.add("jboss-brms"); } private String kieApplication; private int httpPort = -1; private static int STATUS_REQUEST_DELAY = 1000; private static int STATUS_REQUEST_TIMEOUT = 60000; /** * @param server */ public KieServiceDelegate() { } public void setServer(IServer server) { this.server = server; } public IServer getServer() { return server; } public void setHandler(IKieResourceHandler handler) { this.handler = handler; } public IKieResourceHandler getHandler() { return handler; } private void setHttpCredentials(HttpURLConnection conn) { String creds = getUsername() + ":" + getPassword(); conn.setRequestProperty("Authorization", "Basic " + Base64Util.encode(creds)); } protected String httpGet(String request) throws IOException { String host = getKieRESTUrl(); URL url = new URL(host + "/" + request); Activator.println("[GET] "+url.toString()); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setRequestProperty("Content", "application/json"); setHttpCredentials(conn); String response = new BufferedReader(new InputStreamReader((conn.getInputStream()))).readLine(); Activator.println("[GET] response: "+response); return response; } /** * Send an HTTP DELETE request to the KIE console. * @param request - the request URL fragment; see the Drools REST API * documentation for details * @return the Job ID. This can be used in calls to getJobStatus() to fetch * the completion status of the request. * @throws IOException */ protected String httpDelete(String request) throws IOException { String host = getKieRESTUrl(); URL url = new URL(host + "/" + request); Activator.println("[DELETE] "+url.toString()); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("DELETE"); conn.setRequestProperty("Content", "application/json"); setHttpCredentials(conn); String response = new BufferedReader(new InputStreamReader((conn.getInputStream()))).readLine(); Activator.println("[DELETE] response: "+response); if (conn.getResponseCode() != HttpURLConnection.HTTP_ACCEPTED) { throw new IOException("HTTP DELETE failed : HTTP error code : " + conn.getResponseCode()); } JsonObject jo = JsonObject.readFrom(response); String status = jo.get("status").asString(); if (status != null && !status.isEmpty()) { if (!"APPROVED".equals(status)) throw new IOException("HTTP DELETE failed : Request status code : " + status); } String jobId = jo.get("jobId").asString(); if (jobId != null && !jobId.isEmpty()) return jobId; return response; } /** * Send an HTTP POST request to the KIE console. * * @param request - the request URL fragment; see the Drools REST API * documentation for details * @param body - the JSON object required by the request * @return the Job ID. This can be used in calls to getJobStatus() to fetch * the completion status of the request. * @throws IOException * @throws RuntimeException */ protected String httpPost(String request, JsonObject body) throws IOException, RuntimeException { String host = getKieRESTUrl(); URL url = new URL(host + "/" + request); Activator.println("[POST] "+url.toString()+" body: "+body); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setDoOutput(body!=null); conn.setRequestMethod("POST"); conn.setRequestProperty("Content-Type", "application/json"); setHttpCredentials(conn); if (body!=null) { java.io.OutputStream os = conn.getOutputStream(); Writer writer = new OutputStreamWriter(os, "UTF-8"); body.writeTo(writer); writer.close(); os.flush(); } String response = new BufferedReader(new InputStreamReader((conn.getInputStream()))).readLine(); Activator.println("[POST] response: "+response); if (conn.getResponseCode() != HttpURLConnection.HTTP_ACCEPTED) { throw new IOException("HTTP POST failed : HTTP error code : " + conn.getResponseCode()); } JsonObject jo = JsonObject.readFrom(response); String status = jo.get("status").asString(); if (status != null && !status.isEmpty()) { if (!"APPROVED".equals(status)) throw new IOException("HTTP POST failed : Request status code : " + status); } String jobId = jo.get("jobId").asString(); if (jobId != null && !jobId.isEmpty()) return jobId; return response; } /** * Sends a job status request to the KIE Server. * * @param jobId - the job ID from a previous HTTP POST or DELETE request * @return a string composed of a status word (e.g. "SUCCESS" or * "BAD_REQUEST") followed by a colon delimiter and a detailed * message. * @throws IOException */ public String getJobStatus(final String jobId, final String title) throws IOException, InterruptedException { final AtomicReference<String> ar = new AtomicReference<String>(); IWorkbench wb = PlatformUI.getWorkbench(); IProgressService ps = wb.getProgressService(); try { ps.busyCursorWhile(new IRunnableWithProgress() { public void run(IProgressMonitor pm) throws InterruptedException { pm.beginTask("Waiting for Job "+jobId+":\n\n"+title, STATUS_REQUEST_TIMEOUT); pm.subTask(title); long startTime = System.currentTimeMillis(); long stopTime = startTime; do { try { // send a Job Status request every STATUS_REQUEST_DELAY milliseconds Thread.sleep(STATUS_REQUEST_DELAY); String response = httpGet("jobs/" + jobId); JsonObject jo = JsonObject.readFrom(response); String status = jo.get("status").asString(); String result = jo.get("result").asString(); if ("null".equals(result)) { if (!"SUCCESS".equals(status)) result = null; } if (status!=null && result!=null) ar.set(status + ":" + result); stopTime = System.currentTimeMillis(); pm.worked(STATUS_REQUEST_DELAY); Activator.println("status="+status); Activator.println("result="+result); } catch (Exception e) { e.printStackTrace(); } if (pm.isCanceled()) throw new InterruptedException("Operation canceled"); } while (ar.get()==null && stopTime - startTime < STATUS_REQUEST_TIMEOUT); pm.done(); Activator.println( "\n----------------------------------\n"+ "Job "+jobId+"\n"+title+"\ncompleted in "+(stopTime - startTime) / 1000.0+" sec\n"+ "Status: " + ar.get()+ "\n----------------------------------\n"); } }); } catch (InvocationTargetException e) { e.printStackTrace(); return null; } return ar.get(); } public void deleteJob(String jobId) { try { httpDelete("jobs/" + jobId); } catch (Exception e) { // ignore } } public String getUsername() { return handler.getPreference(IKieConstants.PREF_SERVER_USERNAME, ""); } public String getPassword() { return handler.getPreference(IKieConstants.PREF_SERVER_PASSWORD, ""); } public int getGitPort() { return handler.getPreference(IKieConstants.PREF_SERVER_GIT_PORT, 8001); } public String getKieApplication() { if (kieApplication == null) { String app = handler.getPreference(IKieConstants.PREF_SERVER_KIE_APPLICATION_NAME, null); if (app != null) { try { kieApplication = app; Activator.print("Trying " + getKieRESTUrl() + "..."); httpGet("organizationalunits"); Activator.println("success!"); } catch (Exception e) { Activator.println("not found"); kieApplication = null; } } if (kieApplication == null) { // try to determine the HTTP URL from standard application names for (String s : kieApplicationNames) { try { kieApplication = s; Activator.print("Trying " + getKieRESTUrl() + "..."); httpGet("organizationalunits"); handler.putPreference(IKieConstants.PREF_SERVER_KIE_APPLICATION_NAME, s); Activator.println("success!"); break; } catch (FileNotFoundException e) { Activator.println("not found"); kieApplication = null; } catch (IOException e1) { Activator.println("not authorized"); break; } catch (Exception e2) { Activator.println(e2.getMessage()); kieApplication = null; } } } } return kieApplication; } public void setKieApplication(String kieApplication) { this.kieApplication = kieApplication; } public int getHttpPort() { if (httpPort == -1) { // find the HTTP port for this server. Note that the JBossServer // implementation of Server does not support getServerPorts()! // remove dependency on org.jboss.eclipse.ide.as.core // Object o = getServer().getAdapter(JBossServer.class); // if (o instanceof JBossServer) { // httpPort = ((JBossServer) o).getJBossWebPort(); // } // else { // assume that Server.getServerPorts() actually works! ServerPort[] ports = getServer().getServerPorts(null); for (ServerPort port : ports) { if ("HTTP".equals(port.getProtocol())) { httpPort = port.getPort(); if (httpPort==0) { // assume that this is a JBossServer, in which case // HTTP port is always reported as 0. We will have // to rely on the User Preference setting to fetch // the HTTP port for this server. httpPort = -1; } break; } } } if (httpPort == -1) { // assume that it's 8080 return handler.getPreference(IKieConstants.PREF_SERVER_HTTP_PORT, 8080); } } return httpPort; } public void setHttpPort(int httpPort) { this.httpPort = httpPort; } protected String getKieRESTUrl() { String kieApp = getKieApplication(); if (kieApp==null) { kieApp = handler.getPreference(IKieConstants.PREF_SERVER_KIE_APPLICATION_NAME, null); if (kieApp==null) throw new IllegalArgumentException("Can't determine KIE Application Name"); throw new IllegalArgumentException("KIE Application '"+kieApp+"' is not deployed"); } return "http://" + getServer().getHost() + ":" + getHttpPort() + "/" + kieApp + "/rest"; } }