/******************************************************************************* * Copyright © 2008, 2013 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are 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: * IBM Corporation - initial API and implementation * *******************************************************************************/ package org.eclipse.edt.ide.rui.server; import java.io.BufferedInputStream; import java.io.DataInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.PrintStream; import java.io.UnsupportedEncodingException; import java.net.HttpURLConnection; import java.net.InetSocketAddress; import java.net.Proxy; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketAddress; import java.net.SocketException; import java.net.SocketTimeoutException; import java.net.URL; import java.net.URLDecoder; import java.net.URLEncoder; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.eclipse.core.net.proxy.IProxyData; import org.eclipse.core.net.proxy.IProxyService; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.eclipse.edt.compiler.binding.ITypeBinding; import org.eclipse.edt.gen.javascript.JavaScriptAliaser; import org.eclipse.edt.ide.core.internal.lookup.ProjectBuildPath; import org.eclipse.edt.ide.core.internal.lookup.ProjectBuildPathManager; import org.eclipse.edt.ide.core.internal.lookup.ProjectInfo; import org.eclipse.edt.ide.core.internal.lookup.ProjectInfoManager; import org.eclipse.edt.ide.core.internal.utils.Util; import org.eclipse.edt.ide.rui.editor.IEditorSelectAndRevealer; import org.eclipse.edt.ide.rui.internal.Activator; import org.eclipse.edt.ide.rui.internal.nls.RUINlsStrings; import org.eclipse.edt.ide.rui.utils.DebugFileLocator; import org.eclipse.edt.ide.rui.utils.EGLResource; import org.eclipse.edt.ide.rui.utils.FileLocator; import org.eclipse.edt.ide.testserver.TestServerConfiguration; import org.eclipse.edt.ide.testserver.TestServerManager; import org.eclipse.edt.javart.JSERunUnit; import org.eclipse.edt.javart.Runtime; import org.eclipse.edt.javart.json.TokenMgrError; import org.eclipse.edt.javart.messages.Message; import org.eclipse.edt.javart.resources.StartupInfo; import org.eclipse.edt.javart.services.ServiceUtilities; import org.eclipse.edt.javart.services.servlet.ServletUtilities; import org.eclipse.edt.javart.services.servlet.proxy.ProxyEventHandler; import org.eclipse.edt.javart.services.servlet.proxy.ProxyUtilities; import org.eclipse.edt.javart.services.servlet.proxy.RuiBrowserHttpRequest; import org.eclipse.edt.mof.utils.NameUtile; import org.eclipse.edt.runtime.java.eglx.lang.EDictionary; import org.eclipse.jface.text.IDocument; import org.eclipse.swt.widgets.Display; import org.eclipse.ui.IEditorDescriptor; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.part.FileEditorInput; import org.eclipse.ui.texteditor.IDocumentProvider; import org.eclipse.ui.texteditor.ITextEditor; import org.osgi.framework.Bundle; import org.osgi.framework.ServiceReference; import eglx.http.HttpUtilities; import eglx.http.Request; import eglx.http.Response; import eglx.json.JsonUtilities; import eglx.services.ServiceKind; // TODO: Issue with client locations - we need to be case sensitive in the clientsByLocation map on a case sensitive file system and case insensitive on a case insenistive file system // TODO: Only support __ requests for the page that is set for this server (currentURL) // TODO: Remove the need for JavaScript in the currentURL - requires parsing the domain name and port # out // TODO: Need to capture 'important' events so that we can send them after a client is listening again - although even some events will cause all waiting events to be cleared public class EvServer implements IClientProxy { private ServerSocket serverSocket = null; private Map<Integer, QueueProcessor> contextKeyQueues = new HashMap<Integer, QueueProcessor>(); private Object contextSynchObject = new Object(); private List<IContextResolver> contextResolvers = new ArrayList<IContextResolver>(); private static final String EOL = "\r\n"; private static EvServer instance = null; private static boolean running = true; private static int START_PORT_NUMBER = 5590; private static int portNumber = START_PORT_NUMBER; private int contextKeyInc = 1; private static List<String> keys2Remove = new ArrayList<String>(); static { keys2Remove.add("transfer-encoding"); } public static class Event { public PrintStream ps = null; public Socket socket = null; public String url = null; public RuiBrowserHttpRequest xmlRequest = null; public Integer key = null; public Map arguments = null; } private class ___ProxyHandler extends ProxyEventHandler implements Runnable { private RuiBrowserHttpRequest ruiRequest; private PrintStream ps; private boolean isDedicated; public ___ProxyHandler( RuiBrowserHttpRequest ruiRequest, final PrintStream ps ) { this.ruiRequest = ruiRequest; this.ps = ps; } protected HttpURLConnection getHttpProxyConnection( Request restRequest ) throws IOException { String urlString = restRequest.uri; Proxy proxy = getProxy( urlString ); if( proxy != null ) { return (HttpURLConnection)new URL(urlString).openConnection( proxy ); } return null; } private Proxy getProxy( String urlString ) throws IOException { Bundle bundle = Platform.getBundle( "org.eclipse.core.net" ); ServiceReference ref = bundle.getBundleContext().getServiceReference( IProxyService.class.getName() ); if( ref != null ){ Object proxyService = bundle.getBundleContext().getService( ref ); if( proxyService instanceof IProxyService && (((IProxyService)proxyService).isProxiesEnabled() || ((IProxyService)proxyService).isSystemProxiesEnabled()) ) { IProxyData proxyData = getProxyData( (IProxyService)proxyService, urlString ); if( proxyData != null ){ int port = proxyData.getPort() == -1 ? 0 : proxyData.getPort(); SocketAddress address = new InetSocketAddress( proxyData.getHost(), port ); Proxy.Type proxyType; if( proxyData.getType().equals( IProxyData.SOCKS_PROXY_TYPE ) ){ proxyType = Proxy.Type.SOCKS; } else{ proxyType = Proxy.Type.HTTP; } return new Proxy( proxyType, address ); } } } return null; } private IProxyData getProxyData( IProxyService proxyService, String urlString ){ IProxyData proxy = null; IProxyData[] proxies = proxyService.getProxyDataForHost( urlString ); if( proxies != null && proxies.length > 0 ){ for( int idx = 0; idx < proxies.length; idx++ ){ /* * first try to use https * then http * then socks */ if( proxies[idx].getType() == IProxyData.HTTPS_PROXY_TYPE ){ proxy = proxies[idx]; break; } else if( proxies[idx].getType() == IProxyData.HTTP_PROXY_TYPE && (proxy == null || proxy.getType() == IProxyData.SOCKS_PROXY_TYPE ) ){ proxy = proxies[idx]; } else if( proxies[idx].getType() == IProxyData.SOCKS_PROXY_TYPE && proxy == null ){ proxy = proxies[idx]; } } } return proxy; } public void run(){ runProxy( ruiRequest, ps ); } private void runProxy(RuiBrowserHttpRequest ruiRequest, final PrintStream ps) { String urlString = ruiRequest.getURL(); try{ Request innerRequest = null; if( ruiRequest.getHeaders() != null && ruiRequest.getHeaders().containsKey(ProxyUtilities.EGL_REST_CALL) ){ eglRestService(urlString, ruiRequest, ps, innerRequest); } else { innerRequest = ServletUtilities.createHttpRequest(ruiRequest.getContent()); eglRestService(urlString, ruiRequest, ps, innerRequest); } } catch (Throwable e) { System.err.println(urlString); e.printStackTrace(); try { fail(ps); } catch (Exception ee) { } } finally { if(ps != null) { ps.flush(); ps.close(); } } } private void eglRestService(String urlString, RuiBrowserHttpRequest ruiRequest, final PrintStream ps, Request innerRequest) { ServiceKind serviceKind = innerRequest != null && ProxyUtilities.isSoapCall( innerRequest ) ? ServiceKind.WEB : ServiceKind.REST; try { Integer intContextKey = null; if (ruiRequest.getArguments().containsKey("contextKey")) { intContextKey = new Integer((String)ruiRequest.getArguments().get("contextKey")); } else { int idx = ruiRequest.getContent().indexOf( "contextKey=" ); if (idx != -1) { try { intContextKey = new Integer( ruiRequest.getContent().substring( idx + 11 /* "contextKey".length() */ ) ); } catch ( Exception e ) { // Perhaps the body contains the string "contextKey=<stuff>". } } } Request request = null; isDedicated = innerRequest != null && ProxyUtilities.isEGLDedicatedCall(innerRequest); boolean canUseTestServer = (isDedicated && !isDesignPane(intContextKey)) || isPreviewPane(intContextKey); if (!canUseTestServer) { IContext context = findContext(intContextKey); if (context instanceof IContext2) { canUseTestServer = ((IContext2)context).useTestServer(); } } if (canUseTestServer) { IProject project = null; if (isDedicated) { project = ResourcesPlugin.getWorkspace().getRoot().getProject( getProjectName( urlString ) ); } else { // Should be format "workspace://project/pkg.ServiceName" String innerURI = innerRequest.uri; if (innerURI.startsWith("workspace://")) { int start = 12; // "workspace://".length() int end = innerURI.indexOf('/', start); if (end != -1) { project = ResourcesPlugin.getWorkspace().getRoot().getProject(innerURI.substring(start, end)); } } } if (project != null && project.isAccessible()) { TestServerConfiguration config = TestServerManager.getInstance().getServerConfiguration(project, true); config.start(null, true); // Will no-op if already running String encodedProject = project.getName(); try { encodedProject = URLEncoder.encode(encodedProject, "UTF-8"); } catch (UnsupportedEncodingException e) { // Shouldn't happen. } String testServerURI = "http://localhost:" + config.getPort() + "/" + encodedProject; request = new Request(); request.body = ruiRequest.getContent(); request.method = HttpUtilities.convert( ruiRequest.getMethod() ); EDictionary headers = new EDictionary(); Map<String,String> headerMap = ruiRequest.getHeaders(); for(Map.Entry<String, String> entry : headerMap.entrySet()) { headers.put(entry.getKey(), entry.getValue()); } request.headers = headers; if (isDedicated) { serviceKind = ServiceKind.EGL; testServerURI += "/___proxy"; request.uri = testServerURI; // Need to use the outer request as the inner so that we can get to the proxy to do the actual invocation. innerRequest = request; } else { // Modify the inner request to point to the test server testServerURI += "/restservices/"; String innerURI = innerRequest.uri; int start = innerURI.indexOf('/', 12 /* "workspace://".length() */); if (start != -1) { testServerURI += innerURI.substring(start + 1); innerRequest.uri = testServerURI; } } } } if (request == null) { request = new Request(); request.uri = ruiRequest.getURL(); request.body = ruiRequest.getContent(); request.method = HttpUtilities.convert( ruiRequest.getMethod() ); EDictionary headers = new EDictionary(); Map<String,String> headerMap = ruiRequest.getHeaders(); for(Map.Entry<String, String> entry : headerMap.entrySet()) { headers.put(entry.getKey(), entry.getValue()); } request.headers = headers; } Response response = super.runProxy( urlString, request, innerRequest ); if( response != null ) { ps.print( isDedicated ? ProxyUtilities.convert( response.getHeaders(), EOL ) : getResponseHeader( urlString, getContentType(urlString), !urlString.endsWith(".egl"), response.status, response.statusMessage ) ); String content = response.body; ps.write( content.getBytes("utf-8") ); } } catch( IOException ioe ) { String errorMsg = JsonUtilities.createJsonAnyException(ServiceUtilities.buildServiceInvocationException(Message.SOA_E_WS_PROXY_COMMUNICATION, new String[]{urlString}, ioe, serviceKind)); ps.print( getBadResponseHeader() ); ps.print( errorMsg ); } catch(Throwable t) { String errorMsg = JsonUtilities.createJsonAnyException(ServiceUtilities.buildServiceInvocationException(Message.SOA_E_WS_PROXY_UNIDENTIFIED, new Object[0], t, serviceKind)); ps.print( getBadResponseHeader() ); ps.print( errorMsg ); } } @Override protected boolean isEGLDedicatedCall(Request request) { // We always run dedicated services in a remote test server. Return false so we don't try to run it locally. return false; } @Override protected void setBody(Response outerResponse, Response innerResponse) { if (isDedicated) { outerResponse.body =innerResponse.body; outerResponse.initHeader(); outerResponse.getHeaders().putAll(innerResponse.getHeaders()); updateHeaders(outerResponse); outerResponse.addHeader("Server", "EGL Rich UI Server"); outerResponse.addHeader("Pragma", "no-cache"); outerResponse.addHeader("CacheControl", "no-cache"); outerResponse.addHeader("Expires", "-1"); if(!outerResponse.getHeaders().containsKey("Content-Type")){ outerResponse.addHeader("Content-Type", "text/html;charset=utf-8"); } } else { super.setBody(outerResponse, innerResponse); } } private void updateHeaders(Response response){ Pattern p = Pattern.compile("(?i)http/[0-9].[0-9]"); List<Object> keys = new ArrayList<Object>(); if(response.getHeaders() != null){ boolean fixedStatus = false; for( Map.Entry<?, ?> entry : response.getHeaders().entrySet()) { if(!fixedStatus && entry.getKey() == null || entry.getKey().toString() == null) { if(entry.getValue() instanceof List<?>){ for(int idx = 0; idx < ((List<?>)entry.getValue()).size(); idx++){ //the status must appear first, so insert it at the beginning Matcher m = p.matcher(((List<String>)entry.getValue()).get(idx)); if(m.find()){ //we support HTTP/1.0 ((List<String>)entry.getValue()).set(idx, m.replaceFirst(ProxyUtilities.HTTP10STATUS)); fixedStatus = true; } } } } else if(isDedicated && entry.getKey() != null && keys2Remove.contains(entry.getKey().toString().toLowerCase())){ keys.add(entry.getKey()); } } for(Object key : keys){ response.getHeaders().remove(key); } } } private String getProjectName(String url) { int indx = url.indexOf("___proxy"); if (indx > 0) { String name = url.substring(0, indx); name = name.replace("/", ""); name = name.replace("\\", ""); name = name.trim(); try { return URLDecoder.decode(name, "UTF-8"); } catch (UnsupportedEncodingException e) { // Shouldn't happen. return name; } } return ""; } } /** * Process the event queue */ private class QueueProcessor extends Thread { // private LinkedList eventQueue = new LinkedList(); private Integer contextKey = null; private String eventPending = null; private IContext context = null; private int timeLeft = -1; // once the lifeline becomes 0 the thread is killed - initialized in constructor private int lifeLine = -1; // set true once the queue has processed at least 1 __getevent private boolean getProcessed = false; private boolean active = true; private void resetLifeLine() { this.lifeLine = 25; } public IContext getContext() { return context; } public void setContext(IContext context) { this.context = context; } public Integer getContextKey() { return contextKey; } public void setContextKey(Integer contextKey) { this.contextKey = contextKey; } public String getEventPending() { return eventPending; } public void setEventPending(String eventPending) { this.eventPending = eventPending; } public void addEvent(Event event) { this.eventQueue.add(event); } private void resetTimeLeft() { this.timeLeft = 10; } public void setActive( boolean state ) { this.active = state; } /** * */ public QueueProcessor() { super("Visual Editor Event Queue Processor"); resetTimeLeft(); resetLifeLine(); } /* * */ public void run() { while (active) { if (eventQueue.isEmpty() == false) { resetLifeLine(); // If there is a getevent followed by another event, // then immediately respond to the getevent // -------------------------------------------------- while (eventQueue.size() > 1 && ((Event)eventQueue.get(0)).url.indexOf("___getevent") >= 0) { sendEvent((Event)eventQueue.get(0), context, ""); eventQueue.remove(); resetTimeLeft(); } Event event = (Event)eventQueue.get(0); // Process a getevent that is the only entry in the queue if (event.url.indexOf("___getevent") >= 0) { this.getProcessed = true; // Check for a pending event to be sent if (eventPending != null) { sendEvent(event, context, eventPending); eventPending = null; eventQueue.remove(); resetTimeLeft(); } else { // Reduce the wake-up count-down and wake up the // browser // if count down is complete timeLeft--; if (timeLeft <= 0) { sendEvent(event, context, ""); eventQueue.remove(); resetTimeLeft(); } else { try { Thread.sleep(1000); } catch (Exception e) { } } } } else { // Process all other events try { parse4Event(event); eventQueue.remove(); resetTimeLeft(); } catch (Exception ex) { ex.printStackTrace(); } catch (TokenMgrError ex) { ex.printStackTrace(); } finally { event.ps.close(); try { event.socket.close(); } catch (IOException ex) { } } } try { Thread.sleep(10); } catch (InterruptedException ex) { } } else { try { /* * eventQueue is empty */ if (this.getProcessed) { if (this.lifeLine <= 0) { //debug("contextQueue stoped - key: " + this.contextKey.toString()); //TODObreak; } this.lifeLine--; } Thread.sleep(10); } catch (InterruptedException e) { } } } while (!eventQueue.isEmpty()) { Event event = (Event)eventQueue.remove(); if(event.ps != null){ event.ps.close(); } if(event.socket != null){ try { event.socket.close(); } catch (IOException e) { e.printStackTrace(); } } } } /** * Sends the content to the list of clients associated with the given * context. */ private void sendEvent(Event event, IContext context, String content) { event.ps.print(getGoodResponseHeader("", getContentType(""), false)); event.ps.print(content); event.ps.flush(); if (event.ps.checkError()) { debug("sendEvent: error in send context:"+ context.getKey()+ ", content: " + content); } else { //debug("sendEvent: no error - content: " + content); } event.ps.close(); try { event.socket.close(); } catch (IOException ex) { } if (content != null && content.length() > 0) debug("Sent [" + content + "] for [" + event.url + "]"); } } public static EvServer getInstance() { if (instance == null) { instance = new EvServer(); } return instance; } static byte[] getResource(String name) { try { DataInputStream is = new DataInputStream(EvServer.class.getResourceAsStream(name)); byte bytes[] = new byte[is.available()]; is.readFully(bytes); return bytes; } catch (Exception e) { e.printStackTrace(); return new byte[0]; } } static String unescape(String msg) { try { return URLDecoder.decode(msg, "UTF-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); return msg; } } EvServer() { super(); do { try { portNumber = START_PORT_NUMBER++; serverSocket = new ServerSocket(portNumber); } catch (Exception e) { } } while (serverSocket == null); debug("Server ready for business at " + serverSocket); new Thread() { public void run() { startServer(); } }.start(); } /* * (non-Javadoc) * * @see * com.ibm.etools.egl.rui.visualedtor.server.IClientProxy#addPreviewContext( * com.ibm.etools.egl.rui.visualedtor.server.PreviewContext) * * Function called before the first request made */ public void addContext(IContext context) { synchronized(contextSynchObject) { // shouldn't!! if (contextKeyQueues.containsKey(context.getKey())) { // QueueProcessor q = contextKeyQueues.get(context.getKey()); // q.setContext(context); //debug("addContext: context key already present?"); } else { QueueProcessor q = new QueueProcessor(); contextKeyQueues.put(context.getKey(), q); q.setContextKey(context.getKey()); q.setContext(context); q.start(); } } } public void addContextResolver(IContextResolver resolver) { synchronized(contextSynchObject) { if (!contextResolvers.contains(resolver)) { contextResolvers.add(resolver); } } } public void removeContextResolver(IContextResolver resolver) { synchronized(contextSynchObject) { contextResolvers.remove(resolver); } } private void debug(String string) { // System.err.println(new Date() + string); } public void fail(PrintStream ps) throws InterruptedException { ps.print(getBadResponseHeader()); ps.close(); } public String getBadResponseHeader() { return "HTTP/1.0 404 " + EOL + "Content-Type: " + getContentType(".txt") + EOL + EOL; } public String getContentType(String url) { String result = (String) HttpUtilities.getContentType(getExtension(url)); if (result == null) { result = "text/html;charset=utf-8"; } return result; } private String getExtension(String url) { return url.substring(url.lastIndexOf('.') + 1).toLowerCase(); } public String getGoodResponseHeader(String url, String contentType, boolean cache) { return getResponseHeader( url, contentType, cache, HttpUtilities.HTTP_STATUS_OK, HttpUtilities.HTTP_STATUS_MSG_OK ); } private String getResponseHeader(String url, String contentType, boolean cache, int status, String statusMsg ) { return "HTTP/1.0 " + String.valueOf( status ) + " " + statusMsg + EOL + "Server: EGL Rich UI Server" + EOL + (false ? ("CacheControl: max-age=3600" + EOL + "Expires: Fri, 30 Oct 2007 14:19:41 GMT" + EOL) : ("Pragma: no-cache" + EOL + "CacheControl: no-cache" + EOL + "Expires: -1" + EOL)) + "Content-Type: " + contentType + EOL + EOL; } public int getPortNumber() { return portNumber; } public int generateContextKey() { contextKeyInc++; return contextKeyInc; } private boolean checkEventTypes(String url) { boolean result = false; if (url.indexOf("__getversion") > 0) { result = true; } else if (url.indexOf("___startup") > 0) { result = true; } else if (url.indexOf("___traceEvents") > 0) { result = true; } else if (url.indexOf("___widgetPositions") > 0) { result = true; } else if (url.indexOf("___openFile") > 0) { result = true; } else { debug("unknown event type: " + url); } return result; } /** * Places all browser input events on an event queue for sequential * processing */ protected void handleBrowserEvent(final Socket socket) { //debug("EvServer.handleBrowserEvent"); try { String url = null; PrintStream ps = new PrintStream(socket.getOutputStream()); try { RuiBrowserHttpRequest xmlRequest = null; try { xmlRequest = RuiBrowserHttpRequest.createNewRequest(socket); } catch (SocketTimeoutException e) { return; } if (xmlRequest != null) { url = xmlRequest.getURL(); debug("-->EvServer.handleBrowserEvent :" + url + " | ctx="+xmlRequest.getArguments().get("contextKey")); // Proxy and must be checked first since the context key is passed along for debug. if (url.indexOf("__proxy") != -1 || xmlRequest.getHeaders() != null && xmlRequest.getHeaders().containsKey(ProxyUtilities.EGL_REST_CALL) ){ Event event = new Event(); event.ps = ps; event.socket = socket; event.url = xmlRequest.getURL(); event.xmlRequest = xmlRequest; event.arguments = xmlRequest.getArguments(); ___ProxyHandler hop = new ___ProxyHandler( xmlRequest, ps ); Thread thread = new Thread( hop ); thread.start(); } else { // Delay invoking getContextArguments() until after we check if it's a service request. Map args = null; if (xmlRequest.getArguments().containsKey("contextKey")) { args = xmlRequest.getArguments(); } else if (xmlRequest.getContentArguments() != null && xmlRequest.getContentArguments().containsKey("contextKey")) { args = xmlRequest.getContentArguments(); } else { args = xmlRequest.getArguments(); } String strContextKey = (String) args.get("contextKey"); if (strContextKey != null && strContextKey.length() != 0) { // search contextKey try { Integer intContextKey = Integer.valueOf(strContextKey); //debug("handleBrowserEvent url: " + url + " - key: " + intContextKey.toString()); IContext context = findContext(intContextKey); if (context instanceof IContext2) { Event event = new Event(); event.ps = ps; event.socket = socket; event.url = xmlRequest.getURL(); event.xmlRequest = xmlRequest; event.key = intContextKey; event.arguments = args; ((IContext2)context).handleEvent(event); } else if (this.containsKey(intContextKey)) { QueueProcessor q = findQueueProcessor(intContextKey); // Event event = new Event(); event.ps = ps; event.socket = socket; event.url = xmlRequest.getURL(); event.xmlRequest = xmlRequest; event.key = intContextKey; event.arguments = args; // //debug(xmlRequest.getURL()); // debug("handleBrowserEvent: Add event to Q("+intContextKey+"):"+event.url); q.addEvent(event); } else { debug("handleBrowserEvent: context key not in queue"); ps.print(getBadResponseHeader()); //TODO how to make this extensible? if (url.indexOf("___getevent") == -1 && url.indexOf("___debugTerminate") == -1) { // Don't send this msg for certain requests. String msg = RUINlsStrings.bind(RUINlsStrings.ContextKeyNotFound, intContextKey); try { ps.write(msg.getBytes("UTF-8")); } catch (UnsupportedEncodingException uee) { ps.write(msg.getBytes()); } }else if(url.indexOf("___getevent") >0){ debug("__getevent : terminated"); ps.print("context terminated"); ps.flush(); } ps.close(); } } catch (NumberFormatException ex) { debug("contextKey not cast"); } } else if (url.indexOf("___openFile") != -1) { openFile(url, (String) args.get("file"), Integer.parseInt((String) args.get("line")), ps); } else if (url.indexOf("___loadScript") != -1) { loadScript((String) args.get("fileName"), ps); ps.flush(); ps.close(); } else { //debug("no context key: " + url); // initial load external browser if (url.indexOf(".htm") > 0) { // this would only occur if loading from external browser with no context key // ?contextKey=7 Integer key = new Integer(this.generateContextKey()); loadFile(url/* + "?contextKey=" + key */, null, ps); debug("html file loaded with context key: " + key); } else if (this.checkEventTypes(url)) { // do nothing - case handled above } else { debug("handleBrowserEvent unkown url: " + url); loadFile(url, null, ps); } /* * debug("no contextKey - setup the event, generate new key" * ); Integer key = generateContextKey(); Event event = * new Event(); event.ps = ps; event.socket = socket; * event.url = xmlRequest.getURL(); event.xmlRequest = * xmlRequest; event.key = key; QueueProcessor q = new * QueueProcessor(); contextKeyQueues.put(key, q); * q.setContextKey(key); q.start(); q.addEvent(event); */ } } } } catch (SocketException e) { e.printStackTrace(); // ignore } catch (Throwable e) { System.err.println(url); e.printStackTrace(); try { fail(ps); } catch (Exception ee) { } } } catch (Exception e) { e.printStackTrace(); } } /** * * @param url * @param intKey * @param ps */ public void loadFile(String url, Integer intKey, PrintStream ps) { //debug("loadFile " + url); IContext context = null; File file = null; try { String trimmedURL = url.trim(); byte[] bytes = null; if (bytes == null) { String uri = trimmedURL.substring(trimmedURL.indexOf('/') + 1, trimmedURL.length()); if (intKey != null) { context = findContext(intKey); } if (context != null) { IServerContentProvider contentProvider = context.getContentProvider(); bytes = contentProvider.loadContent(uri); } else { SavedContentProvider contentProvider = new SavedContentProvider(); bytes = contentProvider.loadContent(uri); } } if (bytes != null) { ps.print(getGoodResponseHeader(trimmedURL, getContentType(trimmedURL), !trimmedURL.endsWith(".egl"))); ps.write(bytes); ps.flush(); ps.close(); } else { ps.print(getGoodResponseHeader(url, getContentType(url), !url.endsWith(".egl"))); if (file != null) { ps.println("document.write(\"Could not open " + file.getAbsolutePath().replace('\\', '/') + "\");"); } //TODO EDT deployment // else if (trimmedURL.endsWith(DeploymentDescriptorFileUtil.JS_SUFFIX)) { // String uri = trimmedURL.substring(trimmedURL.indexOf('/') + 1, trimmedURL.length()); // ps.println("document.write(\"Could not find or open deployment descriptor " + DeploymentDescriptorFileUtil.convertToEglddFile(uri) + "\");"); // } else { ps.println("document.write(\"Could not open " + url + "\");"); } ps.flush(); ps.close(); } } catch (Exception e) { ps.print(getGoodResponseHeader(url, getContentType(url), !url.endsWith(".egl"))); ps.println("document.write(\"Could not open " + url + "\");"); e.printStackTrace(); ps.flush(); ps.close(); } } private boolean containsKey(Integer key) { synchronized(contextSynchObject) { return contextKeyQueues.containsKey(key); } } /** * This was made synchronized because this was getting called while we were * still processing the last one. * * Function called when the browser makes a connection to the server * */ protected void parse4Event(Event event) throws Exception { // debug( "EvServer.parse4Event entry" + url ); String url = event.url; PrintStream ps = event.ps; try { final Map args = event.arguments; Integer intContextKey = event.key; if (intContextKey != null) { if (!this.containsKey(intContextKey)) { debug("context key: not in queue // url: " + url); } else { // context key is present :) debug("context key: " + intContextKey + " // url: " + url); if (getExtension(url).equals("egl")) { debug("Egl REST service: " + url); // loadEGLService(url, args, ps, request); } else if (url.indexOf("__getversion") != -1) { sendVersion(ps); } // This happens every five seconds as determined by // ClientStore.Client.waitForEvent //---------------------------------------------------------- // -------- // --------------- // else if( url.indexOf( "___getevent" ) != -1 ) { // // debug( "About to call sendEvent" ); // sendEvent( ps, url, intContextKey ); // } // else if (url.indexOf("__println") != -1) { // println(ps, (String) args.get("msg")); // } else if (url.indexOf("___startup") != -1) { debug("parse4event: startup called"); ps.print(getGoodResponseHeader("", "text/html", false)); ps.flush(); ps.close(); } else if (url.indexOf("___traceEvents") != -1) { ps.print(getGoodResponseHeader("", getContentType(""), false)); ps.print("OK"); ps.flush(); ps.close(); } else if (url.indexOf("___widgetPositions") != -1) { /* debug("EvServer.parse4event ___widgetPositions"); String str = event.xmlRequest.getContent().substring( "value=".length()); int indexOfAmp = str.lastIndexOf('&'); if (indexOfAmp != -1) { str = str.substring(0, indexOfAmp); } */ widgetPositions(url, intContextKey, args.get("value").toString()); ps.print(getGoodResponseHeader("", "text/html", false)); ps.flush(); ps.close(); } else if (url.indexOf("___openFile") != -1) { openFile(url, (String) args.get("file"), Integer.parseInt((String) args.get("line")), ps); } else if (url.indexOf("___showFile") != -1) { int offset = Integer.parseInt((String) args.get("offset")); int length = Integer.parseInt((String) args.get("length")); showFile(intContextKey, offset, length, ps); } else if (url.indexOf("___reloadHandler") != -1) { String fileName = (String)args.get("fileName"); fileName = JavaScriptAliaser.packageNameAlias(fileName.substring(0, fileName.length() - 3).split("[/]"), '/', false); loadFile(fileName + ".js", intContextKey, ps); ps.flush(); ps.close(); } else if (url.indexOf("..") == -1) { // server, please // Integer key = // Integer.valueOf(((String)args.get("contextKey"))); debug("parse4event: loadFile"); loadFile(url, intContextKey, ps); } else { fail(ps); } } } } finally { // debug( "EvServer.parse4Event exit" + url ); } } private void loadScript(String fileName, PrintStream ps) { if (fileName.charAt(0) == '/') fileName = fileName.substring(1); debug("load Script "+fileName); String projectName = fileName.substring(0, fileName.indexOf("/")); fileName = fileName.substring(projectName.length()+1); byte[] result; IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName); try{ FileLocator resourceLocator = new DebugFileLocator(project); EGLResource resource = resourceLocator.findResource( fileName ); if ( resource == null || !resource.exists() ) { try { ps.write( ("egl.printError( 'Missing:" + fileName + "');").getBytes() ); } catch ( IOException ioe ) { } return; } InputStream fileContents = new BufferedInputStream(resource.getInputStream()); try{ result = new byte[fileContents.available()]; fileContents.read(result); ps.write(result); }finally{ fileContents.close(); } }catch(Exception e){ try { ps.write( "throw(\"error\");".getBytes() ); } catch ( IOException ioe ) { } } } private void openFile(String url, String fileName, final int line, PrintStream ps) { try{ url = url.trim(); if (url.charAt(0) == '/') { url = url.substring( 1 ); } if (fileName.charAt(0) == '/') { fileName = fileName.substring(1); } debug("open File "+fileName); String projectName = url.substring(0, url.indexOf("/")); IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName); final IFile file = findFileInPath(project, fileName, new HashSet<IProject>()); if (file == null) { debug("no such file: "+fileName+" inside project "+projectName); } else Display.getDefault().asyncExec(new Runnable() { public void run() { IWorkbenchPage page = EvEditorProvider.workbenchPage; IEditorDescriptor desc = PlatformUI.getWorkbench().getEditorRegistry().getDefaultEditor(file.getName()); if (desc == null) desc = PlatformUI.getWorkbench().getEditorRegistry().getDefaultEditor("foo.txt"); try { IEditorPart editorPart = page.openEditor(new FileEditorInput(file), desc.getId()); if (editorPart instanceof IEditorSelectAndRevealer) { IEditorSelectAndRevealer editor = (IEditorSelectAndRevealer) editorPart; editor.selectAndRevealLine(line); } else if (editorPart instanceof ITextEditor) { ITextEditor editor = (ITextEditor) editorPart; IDocumentProvider provider= editor.getDocumentProvider(); IDocument document= provider.getDocument(editor.getEditorInput()); int start= document.getLineOffset(line-1); int end= document.getLineOffset(line); editor.selectAndReveal(start, end-start); } } catch (Exception e) { e.printStackTrace(); } } }); } catch (Exception e) { e.printStackTrace(); }finally{ ps.print(getGoodResponseHeader("", "text/html", false)); ps.close(); } } private IFile findFileInPath(IProject project, String fileName, Set<IProject> seen) { if (seen.contains(project)) { return null; } seen.add(project); String[] segments = fileName.split( "/" ); String name = segments[segments.length - 1]; int dot = name.lastIndexOf('.'); if (dot != -1) { name = name.substring(0, dot); } name = NameUtile.getAsName(name); String[] temp = new String[segments.length - 1]; System.arraycopy(segments, 0, temp, 0, temp.length); String pkg = NameUtile.getAsName(Util.stringArrayToQualifiedName(temp)); ProjectInfo info = ProjectInfoManager.getInstance().getProjectInfo(project); if (info.hasPart(pkg, name) != ITypeBinding.NOT_FOUND_BINDING) { IFile file = info.getPartOrigin(pkg, name).getEGLFile(); if (file != null && file.exists()) { return file; } } // Try the projects on its egl path ProjectBuildPath path = ProjectBuildPathManager.getInstance().getProjectBuildPath(project); for (IProject req : path.getRequiredProjects()) { IFile file = findFileInPath(req, fileName, seen); if (file != null) { return file; } } return null; } protected void println(PrintStream ps, String msg) { msg = unescape(msg); ps.print(getGoodResponseHeader("", getContentType(""), false)); ps.flush(); ps.close(); } public void refreshBrowserContents(final IContext context, final String location, final long modificationStamp, final boolean force) { debug("refreshBrowserContents call"); /* * Display.getDefault().asyncExec(new Runnable() { public void run() { * String eventPending = "egl.reloadPage('" + location + "', "+ * modificationStamp + ", " + force + ")"; if * (contextKeyQueues.containsKey(context.getKey())) { QueueProcessor q = * contextKeyQueues.get(context.getKey()); * q.setEventPending(eventPending); } else { * debug("refreshBrowserContents: context key found no queue" * ); } } }); */ } public void refreshBrowserIncremental(IContext context) { if (this.containsKey(context.getKey())) { try { QueueProcessor q = (QueueProcessor)findQueueProcessor(context.getKey()); String result = "egl.evTerminateReloadHandler();"; q.setEventPending(result); } catch(Exception e) { Activator.getDefault().getLog().log(new Status(Status.ERROR, Activator.PLUGIN_ID, "Refresh Browser Incr: Error", e)); } } } public void doWidgetClick(IContext context, int x, int y) { if (this.containsKey(context.getKey())) { try { QueueProcessor q = (QueueProcessor)findQueueProcessor(context.getKey()); String result = "egl.doWidgetClick(" + x + "," + y + ");"; q.setEventPending(result); } catch(Exception e) { Activator.getDefault().getLog().log(new Status(Status.ERROR, Activator.PLUGIN_ID, "Refresh Browser Incr: Error", e)); } } } public void changeProperty(IContext context, int x, int y, int w, int h, String property, String value,int totalCharactersChanged ) { if (this.containsKey(context.getKey())) { String trimmedURL = context.getUrl().trim(); try { URL jURL = new URL(trimmedURL); String uri = jURL.getPath(); uri = uri.substring(uri.indexOf('/') + 1,uri.length()); QueueProcessor q = (QueueProcessor)findQueueProcessor(context.getKey()); property = property.substring(0,1).toUpperCase() + property.substring( 1 ); String result = "egl.evUpdateWidgetProperty(" + x + "," + y + "," + w + "," + h + ", \"" + property + "\", \"" + value +"\"," + totalCharactersChanged + ");"; q.setEventPending(result); } catch(Exception e) { Activator.getDefault().getLog().log(new Status(Status.ERROR, Activator.PLUGIN_ID, "Refresh Browser Incr: Error", e)); } } } public void deleteWidget(IContext context, int x, int y, int w, int h, int totalCharactersRemoved) { if (this.containsKey(context.getKey())) { String trimmedURL = context.getUrl().trim(); try { URL jURL = new URL(trimmedURL); String uri = jURL.getPath(); uri = uri.substring(uri.indexOf('/') + 1,uri.length()); QueueProcessor q = (QueueProcessor)findQueueProcessor(context.getKey()); String result = "egl.evDeleteWidget(" + x + "," + y + "," + w + "," + h + "," + totalCharactersRemoved + ")"; q.setEventPending(result); } catch(Exception e) { Activator.getDefault().getLog().log(new Status(Status.ERROR, Activator.PLUGIN_ID, "Refresh Browser Incr: Error", e)); } } } public void moveWidget(IContext context, int x, int y, int w, int h, int tx, int ty, int tw, int th, int oldIndex, int newIndex, int[] charactersChanged) { if (this.containsKey(context.getKey())) { String trimmedURL = context.getUrl().trim(); try { URL jURL = new URL(trimmedURL); String uri = jURL.getPath(); uri = uri.substring(uri.indexOf('/') + 1,uri.length()); QueueProcessor q = (QueueProcessor)findQueueProcessor(context.getKey()); String result = "egl.evMoveWidget(" + x + "," + y + "," + w + "," + h + "," + tx + "," + ty + "," + tw + "," + th + "," + oldIndex + "," + newIndex + "," + charactersChanged[0] + "," + charactersChanged[1] + ")"; q.setEventPending(result); } catch(Exception e) { Activator.getDefault().getLog().log(new Status(Status.ERROR, Activator.PLUGIN_ID, "Refresh Browser Incr: Error", e)); } } } public void removeContext(IContext context) { debug("removeContext"); synchronized(contextSynchObject) { QueueProcessor q = (QueueProcessor)contextKeyQueues.remove(context.getKey()); if (q != null) { q.setActive(false); } } } public void requestWidgetPositions(IContext context) { debug("EvServer.requestWidgetPositions"); if (this.containsKey(context.getKey())) { QueueProcessor q = (QueueProcessor)findQueueProcessor(context.getKey()); q.setEventPending("egl.getWidgetPositions();"); } else { debug("requestWidgetPosition: context key found no queue"); } } public void terminateSession(IContext context) { debug("terminateSession called"); if (this.containsKey(context.getKey())) { QueueProcessor q = (QueueProcessor)findQueueProcessor(context.getKey()); q.setEventPending("if (window.egl) egl.terminateSession()"); } else { debug("terminateSession: context key found no queue"); } } public void sendVersion(PrintStream ps) { ps.print(getGoodResponseHeader("", getContentType(""), false)); ps.print("1"); ps.flush(); ps.close(); } public void startServer() { try { // // run with // http://localhost:5498/someURL?arg1=value1&arg2=value2&arg3=value3 // if (Runtime.getRunUnit() == null) { Runtime.setStaticRunUnit( new JSERunUnit( new StartupInfo( "IDERunUnit", "", null ) ) ); } while (running) { final Socket client = serverSocket.accept(); debug("serverSocket accept"); handleBrowserEvent(client); /* * Job eventHandler = new Job("HTTP Event Handler") { protected IStatus run(IProgressMonitor monitor) { return Status.OK_STATUS; } }; eventHandler.setSystem(true); eventHandler.schedule(); */ } } catch (IOException e) { e.printStackTrace(); } } private synchronized void widgetPositions(String url, Integer intKey, String positionInfo) { IContext context = findContext(intKey); if (context instanceof DesignContext) { IServerListener listener = ((DesignContext)context).getServerListener(); if (listener != null) { //System.out.println(positionInfo); listener.acceptWidgetPositions(positionInfo); } } } private IContext findContext(Integer contextKey) { synchronized(contextSynchObject) { QueueProcessor q = (QueueProcessor)contextKeyQueues.get(contextKey); if (q != null) { return q.getContext(); } if (contextResolvers.size() > 0) { for (IContextResolver resolver : contextResolvers) { IContext context = resolver.findContext(contextKey); if (context != null) { return context; } } } } return null; } private QueueProcessor findQueueProcessor(Integer contextKey) { synchronized(contextSynchObject) { return (QueueProcessor)contextKeyQueues.get(contextKey); } } private boolean isDesignPane(Integer contextKey){ IContext context = findContext( contextKey ); return(context instanceof DesignContext); } private boolean isPreviewPane(Integer contextKey){ IContext context = findContext( contextKey ); return(context instanceof PreviewContext); } private synchronized void showFile(Integer intKey, final int offset, final int length, PrintStream ps) { try{ AbstractPreviewContext context = (AbstractPreviewContext)findContext(intKey); if (context != null) { final IServerListener listener = context.getServerListener(); if (listener != null) { Display.getDefault().asyncExec(new Runnable() { public void run() { listener.selectTextInEditor(offset, length); } }); } } }finally{ ps.print(getGoodResponseHeader("", "text/html", false)); ps.close(); } } }