/* * "Copyright (c) 2010-11 The Regents of the University of California. * All rights reserved. * * Permission to use, copy, modify, and distribute this software and its * documentation for any purpose, without fee, and without written agreement is * hereby granted, provided that the above copyright notice, the following * two paragraphs and the author appear in all copies of this software. * * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS." * * Author: Jorge Ortiz (jortiz@cs.berkeley.edu) * IS4 release version 1.1 */ package local.rest.resources; import local.db.*; import local.rest.*; import local.rest.resources.util.*; import local.rest.interfaces.*; import is4.*; import net.sf.json.*; import java.util.*; import java.util.logging.Logger; import java.util.logging.Level; import java.lang.StringBuffer; import java.net.*; import com.sun.net.httpserver.*; import javax.naming.InvalidNameException; import java.io.*; public class SymlinkResource extends Resource{ protected static transient Logger logger = Logger.getLogger(SymlinkResource.class.getPackage().getName()); private String uri_link = null; private URL url_link = null; public SymlinkResource(String path, String uri) throws Exception, InvalidNameException{ super(path); uri_link = uri; //set type TYPE = ResourceUtils.SYMLINK_RSRC; database.setRRType(URI, ResourceUtils.translateType(TYPE).toLowerCase()); if(!database.isSymlink(URI)){ //add to symlink table database.insertNewSymlinkEntry(URI, uri_link); } } public SymlinkResource(String path, URL is4Url) throws Exception, InvalidNameException{ super(path); url_link = is4Url; //set type TYPE = ResourceUtils.SYMLINK_RSRC; database.setRRType(URI, ResourceUtils.translateType(TYPE).toLowerCase()); if(!database.isSymlink(URI)){ //add to symlink table database.insertNewSymlinkEntry(URI, is4Url.toString()); } } public String getLinkString(){ if(uri_link != null) return uri_link; else if(url_link != null) return url_link.toString(); return null; } public void get(HttpExchange exchange, boolean internalCall, JSONObject internalResp){ String tailResources = null; //this symlink points directly to a hardlink if(uri_link != null && !database.isSymlink(uri_link)){ logger.info(uri_link + " is not a symlink"); handleUriSymlinkRequest(exchange, internalCall, internalResp, uri_link); } //this symlink points to another symlink else if (uri_link != null && database.isSymlink(uri_link)){ String linksToStr = database.getSymlinkAlias(uri_link); while(linksToStr !=null && database.isSymlink(linksToStr)){ linksToStr = database.getSymlinkAlias(linksToStr); } if(linksToStr !=null && linksToStr.startsWith("/")) handleUriSymlinkRequest(exchange, internalCall, internalResp, linksToStr); //forward to the sfs instances that this symlink points to else handleUrlSymlinkRequest(exchange, internalCall, internalResp, linksToStr); } //forward to the sfs instances that this symlink points to else if (url_link != null){ handleUrlSymlinkRequest(exchange, internalCall, internalResp, url_link.toString()); } } private void handleUriSymlinkRequest(HttpExchange exchange, boolean internalCall, JSONObject internalResp, String linksTo){ String tailResources = getTailResourceUri(exchange, true); logger.info("tail_resources: " + tailResources); String thisUri = null; if( (linksTo.endsWith("/") && !tailResources.startsWith("/")) || (!linksTo.endsWith("/") && tailResources.startsWith("/")) ) { thisUri = linksTo + tailResources; } else if (linksTo.endsWith("/") && tailResources.startsWith("/")) { tailResources = tailResources.substring(1, tailResources.length()); thisUri = linksTo + tailResources; } else if (!linksTo.endsWith("/") && !tailResources.startsWith("/")) { thisUri = linksTo + "/" + tailResources; } //the request is translated exchangeJSON.put("requestUri", thisUri); logger.info("Setting internalExchange.requestUri=" + thisUri); if(thisUri.contains("*")){ exchange.setAttribute("request_uri", thisUri); handleRecursiveFSQuery(exchange, internalCall, internalResp); } else { Resource resource = RESTServer.getResource(thisUri); if(resource !=null){ resource.exchangeJSON.putAll(this.exchangeJSON); resource.get(exchange, internalCall, internalResp); } else { logger.warning("could not get resource: " + thisUri); sendResponse(exchange, 404, null, internalCall, internalResp); } } } private void handleUrlSymlinkRequest(HttpExchange exchange, boolean internalCall, JSONObject internalResp, String linksToUrl){ try { String tailResources = getTailResourceUri(exchange, true); tailResources = getTailResourceUri(exchange, false); String thisUrl = linksToUrl + tailResources; logger.info("GET " + thisUrl); StringBuffer serverRespBuffer = new StringBuffer(); HttpURLConnection is4Conn = is4ServerGet(thisUrl, serverRespBuffer); if(is4Conn != null){ String requestUri = exchange.getRequestURI().toString(); if(requestUri.contains("?")) requestUri = requestUri.substring(0, requestUri.indexOf("?")); if(requestUri.contains("*") && !requestUri.endsWith("*")) { JSONObject fixedServerResp = new JSONObject(); String localUri = URI + tailResources; fixedServerResp.put(localUri, serverRespBuffer.toString()); sendResponse(exchange, is4Conn.getResponseCode(), fixedServerResp.toString(), internalCall, internalResp); } else { sendResponse(exchange, is4Conn.getResponseCode(), serverRespBuffer.toString(), internalCall, internalResp); } is4Conn.disconnect(); } else { sendResponse(exchange, 504 /* Gateway timeout */, null, internalCall, internalResp); } }catch(Exception e){ logger.log(Level.WARNING, "", e); sendResponse(exchange, 504 /* Gateway timeout */, null, internalCall, internalResp); } } public void put(HttpExchange exchange, String data, boolean internalCall, JSONObject internalResp){ try { String tailResources = null; if(uri_link != null){ tailResources = getTailResourceUri(exchange, true); String thisUri = uri_link + tailResources; Resource resource = RESTServer.getResource(thisUri); resource.exchangeJSON.putAll(this.exchangeJSON); resource.put(exchange, data, internalCall, internalResp); } else if (url_link != null){ tailResources = getTailResourceUri(exchange, false); String thisUrl = url_link.toString() + tailResources; StringBuffer serverRespBuffer = new StringBuffer(); HttpURLConnection is4Conn = is4ServerPut(thisUrl, data, serverRespBuffer); if(is4Conn != null){ String requestUri = exchange.getRequestURI().toString(); if(requestUri.contains("?")) requestUri = requestUri.substring(0, requestUri.indexOf("?")); if(requestUri.contains("*") && !requestUri.endsWith("*")) { JSONObject fixedServerResp = new JSONObject(); String localUri = URI + tailResources; fixedServerResp.put(localUri, serverRespBuffer.toString()); sendResponse(exchange, is4Conn.getResponseCode(), fixedServerResp.toString(), internalCall, internalResp); } else { sendResponse(exchange, is4Conn.getResponseCode(), serverRespBuffer.toString(), internalCall, internalResp); } is4Conn.disconnect(); } else { sendResponse(exchange, 504 /* Gateway timeout */, null, internalCall, internalResp); } } } catch(Exception e){ logger.log(Level.WARNING, "", e); sendResponse(exchange, 504 /* Gateway timeout */, null, internalCall, internalResp); } } public void post(HttpExchange exchange, String data, boolean internalCall, JSONObject internalResp){ try { String tailResources = null; if(uri_link != null){ tailResources = getTailResourceUri(exchange, true); String thisUri = uri_link + tailResources; Resource resource = RESTServer.getResource(thisUri); resource.exchangeJSON.putAll(this.exchangeJSON); resource.post(exchange, data, internalCall, internalResp); } else if (url_link != null){ tailResources = getTailResourceUri(exchange, false); String thisUrl = url_link.toString() + tailResources; StringBuffer serverRespBuffer = new StringBuffer(); HttpURLConnection is4Conn = is4ServerPost(thisUrl, data, serverRespBuffer); if(is4Conn != null){ String requestUri = exchange.getRequestURI().toString(); if(requestUri.contains("?")) requestUri = requestUri.substring(0, requestUri.indexOf("?")); if(requestUri.contains("*") && !requestUri.endsWith("*")) { JSONObject fixedServerResp = new JSONObject(); String localUri = URI + tailResources; fixedServerResp.put(localUri, serverRespBuffer.toString()); sendResponse(exchange, is4Conn.getResponseCode(), fixedServerResp.toString(), internalCall, internalResp); } else { sendResponse(exchange, is4Conn.getResponseCode(), serverRespBuffer.toString(), internalCall, internalResp); } is4Conn.disconnect(); } else { sendResponse(exchange, 504 /* Gateway timeout */, null, internalCall, internalResp); } } } catch(Exception e){ logger.log(Level.WARNING, "", e); sendResponse(exchange, 504 /* Gateway timeout */, null, internalCall, internalResp); } } public void loopDelete(){ logger.info("LoopDelete; Removing symlink entry: " + URI); database.removeSymlinkEntry(URI); database.removeRestResource(URI); RESTServer.removeResource(this); //delete from internal presentation this.metadataGraph.removeNode(this.URI); } public void delete(HttpExchange exchange, boolean internalCall, JSONObject internalResp){ try { logger.info("Symlink delete:" + URI); String tailResources = null; if(uri_link != null){ tailResources = getTailResourceUri(exchange, true); if(tailResources.equals("")){ database.removeSymlinkEntry(URI); database.removeRestResource(URI); RESTServer.removeResource(this); //delete from internal presentation this.metadataGraph.removeNode(this.URI); sendResponse(exchange, 202, null, internalCall, internalResp); return; } //walk the link path to either a hardlink or an external url String linksToStr = uri_link; if(database.isSymlink(linksToStr)){ linksToStr = database.getSymlinkAlias(uri_link); while(linksToStr !=null && database.isSymlink(linksToStr)){ linksToStr = database.getSymlinkAlias(linksToStr); } } //handle the deletion of a hardlink if(linksToStr.startsWith("/")){ String thisUri = uri_link + tailResources; Resource resource = RESTServer.getResource(thisUri); if(resource != null){ resource.exchangeJSON.putAll(this.exchangeJSON); resource.delete(exchange, internalCall, internalResp); }else{ sendResponse(exchange, 404, null, internalCall, internalResp); return; } } //forward the delete to the external link else { String thisUrl = linksToStr + tailResources; StringBuffer serverRespBuffer = new StringBuffer(); HttpURLConnection is4Conn = is4ServerDelete(thisUrl, serverRespBuffer); if(is4Conn != null){ String requestUri = exchange.getRequestURI().toString(); if(requestUri.contains("?")) requestUri = requestUri.substring(0, requestUri.indexOf("?")); if(requestUri.contains("*") && !requestUri.endsWith("*")) { JSONObject fixedServerResp = new JSONObject(); String localUri = URI + tailResources; fixedServerResp.put(localUri, serverRespBuffer.toString()); sendResponse(exchange, is4Conn.getResponseCode(), fixedServerResp.toString(), internalCall, internalResp); } else { sendResponse(exchange, is4Conn.getResponseCode(), serverRespBuffer.toString(), internalCall, internalResp); } is4Conn.disconnect(); } else { sendResponse(exchange, 504 /* Gateway timeout */, null, internalCall, internalResp); } } } else if (url_link != null){ tailResources = getTailResourceUri(exchange, false); if(tailResources.equals("")){ database.removeSymlinkEntry(URI); database.removeRestResource(URI); RESTServer.removeResource(this); //delete from internal presentation this.metadataGraph.removeNode(this.URI); sendResponse(exchange, 202, null, internalCall, internalResp); return; } String thisUrl = url_link.toString() + tailResources; StringBuffer serverRespBuffer = new StringBuffer(); HttpURLConnection is4Conn = is4ServerDelete(thisUrl, serverRespBuffer); if(is4Conn != null){ String requestUri = exchange.getRequestURI().toString(); if(requestUri.contains("?")) requestUri = requestUri.substring(0, requestUri.indexOf("?")); if(requestUri.contains("*") && !requestUri.endsWith("*")) { JSONObject fixedServerResp = new JSONObject(); String localUri = URI + tailResources; fixedServerResp.put(localUri, serverRespBuffer.toString()); sendResponse(exchange, is4Conn.getResponseCode(), fixedServerResp.toString(), internalCall, internalResp); } else { sendResponse(exchange, is4Conn.getResponseCode(), serverRespBuffer.toString(), internalCall, internalResp); } is4Conn.disconnect(); } else { sendResponse(exchange, 504 /* Gateway timeout */, null, internalCall, internalResp); } } } catch(Exception e){ logger.log(Level.WARNING, "", e); sendResponse(exchange, 504 /* Gateway timeout */, null, internalCall, internalResp); } } /** * Get the piece of the string that is ahead of this URI. Remove parameters if symlink is to local resource, * leave the params if this symlink points to another is4 server (url_link is not null). */ private String getTailResourceUri(HttpExchange exchange, boolean removeParams){ String requestUri=null; if(exchangeJSON.containsKey("requestUri")){ requestUri=exchangeJSON.getString("requestUri"); logger.info("Got internalExchange.requestUri=" + requestUri); exchangeJSON.discard("requestUri"); }else{ requestUri = exchange.getRequestURI().toString(); } String myUri = URI; //change the request uri if it's a recursive structural query if(requestUri.contains("*") && requestUri.contains("?")){ String transReqUri = requestUri.substring(0, requestUri.indexOf("?")); if(transReqUri.endsWith("*")){ requestUri = requestUri.replace(transReqUri, URI + "*"); logger.info("Found * query, replacing: " + transReqUri + " with " + URI + "* = requestUri " + requestUri); } else if(transReqUri.contains("*") && !transReqUri.endsWith("*")){ String res = processMidStarReq(transReqUri); if(res != null) requestUri = res; logger.fine("Symlink_:: updated Request URI: " + requestUri); } else if(transReqUri.contains("*") && transReqUri.endsWith("*") && transReqUri.indexOf("*")<transReqUri.lastIndexOf("*")){ String res = processMidStarReq(transReqUri); if(res != null) requestUri = res; logger.fine("Symlink_:: updated Request URI: " + requestUri); } else { requestUri = requestUri.replace(transReqUri, URI); logger.info("Found * query, replacing: " + transReqUri + " with " + URI + "* = requestUri " + requestUri); } } else if(requestUri.contains("*") && !requestUri.contains("?")){ String transReqUri = requestUri; if(transReqUri.endsWith("*")){ requestUri = requestUri.replace(transReqUri, URI + "*"); logger.info("Found * query, replacing: " + transReqUri + " with " + URI + "* = requestUri " + requestUri); } else if(transReqUri.contains("*") && !transReqUri.endsWith("*")){ String res = processMidStarReq(transReqUri); if(res != null) requestUri = res; logger.fine("Symlink_:: updated Request URI: " + requestUri); } else if(transReqUri.contains("*") && transReqUri.endsWith("*") && transReqUri.indexOf("*")<transReqUri.lastIndexOf("*")){ String res = processMidStarReq(transReqUri); if(res != null) requestUri = res; logger.fine("Symlink_:: updated Request URI: " + requestUri); } else { requestUri = requestUri.replace(transReqUri, URI); logger.info("Found * query, replacing: " + transReqUri + " with " + URI + " = requestUri " + requestUri); } } //remove parameters first String paramsStr = null; if(requestUri.contains("?")){ paramsStr = requestUri.substring(requestUri.indexOf("?"), requestUri.length()); requestUri = requestUri.replace(paramsStr, ""); } //remove trailing / for uris if(requestUri.endsWith("/")) requestUri = requestUri.substring(0, requestUri.length()-1); if(myUri.endsWith("/")) myUri = myUri.substring(0, myUri.length()-1); //remove myUri from the request uri String tailResources = requestUri.replace(myUri, ""); logger.info("getTailResourceUri(): Remove: " + myUri + " from " + requestUri + " result: " + tailResources); //if only a / is left, then the request uri and this uri are equal /*if(tailResources.equals("/")) tailResources = "";*/ if(tailResources.startsWith("/")) tailResources = tailResources.substring(1, tailResources.length()); //if we want the parameters left on the uri, put them back on if(!removeParams && paramsStr!=null) tailResources = tailResources.concat(paramsStr); return tailResources; } /** * IS4 get. Returns the connection object and populates the is4Resp string. */ public static HttpURLConnection is4ServerGet(String is4Url, StringBuffer is4Resp){ try { logger.info("Symlink GET: " + is4Url); if(is4Url != null && is4Resp != null){ try{ if(is4Url.startsWith("http://")){ URL is4UrlObj = new URL(is4Url); HttpURLConnection is4Conn = (HttpURLConnection) is4UrlObj.openConnection(); is4Conn.setConnectTimeout(5000); is4Conn.setDoOutput(true); is4Conn.connect(); //Populate reply BufferedReader reader = new BufferedReader(new InputStreamReader(is4Conn.getInputStream())); String line = null; while((line = reader.readLine()) != null) is4Resp.append(line); logger.info("Symlink GET RESPONSE: " + is4Resp.toString()); return is4Conn; } } catch(Exception e){ logger.log(Level.WARNING, "", e); return null; } } logger.warning("is4Url and/or postData is null"); return null; } catch (Exception e){ logger.log(Level.WARNING, "", e); return null; } } /** * IS4 post. Returns the connection object and populates the is4Resp string. */ public static HttpURLConnection is4ServerPost(String is4Url, String postData, StringBuffer is4Resp){ try { if(is4Url != null && postData != null && is4Resp != null){ try{ if(is4Url.startsWith("http://")){ URL is4UrlObj = new URL(is4Url); HttpURLConnection is4Conn = (HttpURLConnection) is4UrlObj.openConnection(); is4Conn.setConnectTimeout(5000); is4Conn.setDoOutput(true); is4Conn.connect(); OutputStreamWriter wr = new OutputStreamWriter(is4Conn.getOutputStream()); wr.write(postData); wr.flush(); //Populate reply BufferedReader reader = new BufferedReader(new InputStreamReader(is4Conn.getInputStream())); String line = null; while((line = reader.readLine()) != null) is4Resp.append(line); return is4Conn; } } catch(Exception e){ logger.log(Level.WARNING, "", e); return null; } } logger.warning("is4Url and/or postData is null"); return null; } catch(Exception e){ logger.log(Level.WARNING, "", e); return null; } } /** * IS4 put. Returns the connection object and populates the is4Resp string. */ public static HttpURLConnection is4ServerPut(String is4Url, String putData, StringBuffer is4Resp){ try{ if(is4Url != null && putData != null && is4Resp != null){ try{ if(is4Url.startsWith("http://")){ URL is4UrlObj = new URL(is4Url); HttpURLConnection is4Conn = (HttpURLConnection) is4UrlObj.openConnection(); is4Conn.setConnectTimeout(5000); is4Conn.setDoOutput(true); is4Conn.setRequestMethod("PUT"); is4Conn.connect(); OutputStreamWriter wr = new OutputStreamWriter(is4Conn.getOutputStream()); wr.write(putData); wr.flush(); //Populate reply BufferedReader reader = new BufferedReader(new InputStreamReader(is4Conn.getInputStream())); String line =null; while((line = reader.readLine()) != null) is4Resp.append(line); return is4Conn; } } catch(Exception e){ logger.log(Level.WARNING, "", e); return null; } } logger.warning("is4Url and/or postData is null"); return null; } catch (Exception e){ logger.log(Level.WARNING, "", e); return null; } } /** * IS4 delete. Returns the connection object and populates the is4Resp string. */ public static HttpURLConnection is4ServerDelete(String is4Url, StringBuffer is4Resp){ try { if(is4Url != null && is4Resp != null){ try{ if(is4Url.startsWith("http://")){ URL is4UrlObj = new URL(is4Url); HttpURLConnection is4Conn = (HttpURLConnection) is4UrlObj.openConnection(); is4Conn.setConnectTimeout(5000); is4Conn.setRequestMethod("DELETE"); is4Conn.connect(); //Populate reply BufferedReader reader = new BufferedReader(new InputStreamReader(is4Conn.getInputStream())); String line = null; while((line = reader.readLine()) != null) is4Resp.append(line); return is4Conn; } } catch(Exception e){ logger.log(Level.WARNING, "", e); return null; } } logger.warning("is4Url and/or postData is null"); return null; } catch(Exception e){ logger.log(Level.WARNING, "", e); return null; } } private String processMidStarReq(String transReqUri){ String resultStr = null; logger.fine("Symlink_:: Request w/out params: " + transReqUri); logger.fine("Symlink_:: Resource URI: " + URI); //replace the starred portion with the portion of this URI // exmaple:: // request: /is4/*/slink/all // this uri: /is4/test/slink/ // result: /is4/test/slink/all Vector<String> reqTokVec = createTokenVector(transReqUri, "*"); Vector<String> uriTokVec = createTokenVector(URI, "/"); if(reqTokVec.size()>0 && uriTokVec.size()>0){ String lastReqElt = (String) reqTokVec.get(reqTokVec.size()-1); String lastUriElt = (String) uriTokVec.get(uriTokVec.size()-1); logger.fine("Symlink_:: Request lastToken: " + lastReqElt); logger.fine("Symlink_:: Resource URI lastToken " + lastUriElt); if(transReqUri.contains(lastUriElt)){ String reqPrefix = transReqUri.substring(0, transReqUri.indexOf(lastUriElt)); String uriPrefix = URI.substring(0, URI.indexOf(lastUriElt)); logger.fine("Symlink_:: Request Prefix: " + reqPrefix); logger.fine("Symlink_:: Resource URI Prefix: " + uriPrefix); resultStr = transReqUri.replace(reqPrefix, uriPrefix); } else { logger.fine("Symlink_:: Resource URI: " + URI + " does NOT match " + transReqUri); } } return resultStr; } private Vector<String> createTokenVector(String target, String delim){ StringTokenizer tokens = new StringTokenizer(target, delim); Vector<String> allToks = new Vector<String>(); while(tokens.hasMoreTokens()) allToks.add((String)tokens.nextToken()); return allToks; } }