Name: gateway-uvi/server
| 1: | #!/usr/bin/ruby |
| 2: | # Apache License, Version 2.0 |
| 3: | # |
| 4: | # Copyright (c) 2013 Juergen Mangler |
| 5: | # |
| 6: | # Licensed under the Apache License, Version 2.0 (the "License"); |
| 7: | # you may not use this file except in compliance with the License. |
| 8: | # You may obtain a copy of the License at |
| 9: | # |
| 10: | # http://www.apache.org/licenses/LICENSE-2.0 |
| 11: | # |
| 12: | # Unless required by applicable law or agreed to in writing, software |
| 13: | # distributed under the License is distributed on an "AS IS" BASIS, |
| 14: | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 15: | # See the License for the specific language governing permissions and |
| 16: | # limitations under the License. |
| 17: | |
| 18: | require 'rubygems' |
| 19: | require 'riddl/server' |
| 20: | require 'json' |
| 21: | require 'xml/smart' |
| 22: | require 'fileutils' |
| 23: | require 'term/ansicolor' |
| 24: | class String |
| 25: | include Term::ANSIColor |
| 26: | end |
| 27: | |
| 28: | class Worker #{{{ |
| 29: | def initialize(*para) |
| 30: | @para = para |
| 31: | end |
| 32: | def list |
| 33: | list = [] |
| 34: | %w{in out}.each do |e| |
| 35: | Dir[File.dirname(__FILE__) + "/#{e}/*"].each do |f| |
| 36: | meta = {} |
| 37: | doc = XML::Smart.open(f + '/xml') |
| 38: | doc.register_namespace 'g', 'http://www.fp7-adventure.eu/xmlSchema/Gateways/' |
| 39: | meta['id'] = File.basename(f) |
| 40: | meta['type'] = e |
| 41: | meta['op'] = doc.find('string(//g:header/g:operationName)') |
| 42: | meta['seq'] = doc.find('string(//g:header/g:commandSequence)') |
| 43: | meta['val'] = doc.find('string(//g:header/g:validity)') |
| 44: | meta.merge!(JSON.parse(File.read(f + '/meta'))) if File.exists?(f + '/meta') |
| 45: | list << meta |
| 46: | end if @para.empty? || @para[0] == e |
| 47: | end |
| 48: | list.sort{|a,b| [a['type'],a['id']] <=> [b['type'],b['id']]} |
| 49: | end |
| 50: | def details |
| 51: | return [] if @para.length == 0 |
| 52: | path = if @para.length == 1 || @para[0].to_i.to_s == @para[0] |
| 53: | 'in' |
| 54: | else |
| 55: | @para.shift == 'in' ? 'in' : 'out' |
| 56: | end |
| 57: | name = File.dirname(__FILE__) + "/#{path}/#{@para[0].to_i}/xml" |
| 58: | File.exists?(name) ? File.read(name) : nil |
| 59: | end |
| 60: | def delete |
| 61: | return "won't do" if @para.length == 0 |
| 62: | path = if @para.length == 1 || @para[0].to_i.to_s == @para[0] |
| 63: | 'in' |
| 64: | else |
| 65: | @para.shift == 'in' ? 'in' : 'out' |
| 66: | end |
| 67: | mess = nil |
| 68: | @para.each do |par| |
| 69: | name = File.dirname(__FILE__) + "/#{path}/#{par[0].to_i}" |
| 70: | if File.exists?(name) |
| 71: | FileUtils.rm_r(name) |
| 72: | else |
| 73: | mess = "some don't exist" |
| 74: | end |
| 75: | end |
| 76: | mess |
| 77: | end |
| 78: | end #}}} |
| 79: | |
| 80: | class Save < Riddl::Implementation |
| 81: | def response |
| 82: | begin |
| 83: | id = id.nil? ? 0 : id + 1 |
| 84: | Dir.mkdir(File.dirname(__FILE__) + "/in/#{id}") |
| 85: | rescue SystemCallError |
| 86: | retry |
| 87: | end |
| 88: | |
| 89: | meta = {:answer => @h['CPEE_CALLBACK'], :time => Time.now.to_s} |
| 90: | File.write(File.dirname(__FILE__) + "/in/#{id}/meta", JSON.pretty_generate(meta)) |
| 91: | File.write(File.dirname(__FILE__) + "/in/#{id}/xml", @p[0].value.read) |
| 92: | @headers << Riddl::Header.new('CPEE_CALLBACK', 'true') |
| 93: | nil |
| 94: | end |
| 95: | end |
| 96: | |
| 97: | class InList < Riddl::Implementation #{{{ |
| 98: | def response |
| 99: | Riddl::Parameter::Complex.new("result","application/json",JSON::pretty_generate(Worker.new('in').list)) |
| 100: | end |
| 101: | end #}}} |
| 102: | class OutList < Riddl::Implementation #{{{ |
| 103: | def response |
| 104: | Riddl::Parameter::Complex.new("result","application/json",JSON::pretty_generate(Worker.new('out').list)) |
| 105: | end |
| 106: | end #}}} |
| 107: | class InDetails < Riddl::Implementation #{{{ |
| 108: | def response |
| 109: | if (det = Worker.new('in',@r[-1]).details).nil? |
| 110: | @status = 404 |
| 111: | else |
| 112: | Riddl::Parameter::Complex.new("gatewayRequest","text/xml",det) |
| 113: | end |
| 114: | end |
| 115: | end #}}} |
| 116: | class OutDetails < Riddl::Implementation #{{{ |
| 117: | def response |
| 118: | if (det = Worker.new('out',@r[-1]).details).nil? |
| 119: | @status = 404 |
| 120: | else |
| 121: | Riddl::Parameter::Complex.new("gatewayResponse","text/xml",det) |
| 122: | end |
| 123: | end |
| 124: | end #}}} |
| 125: | class InDelete < Riddl::Implementation #{{{ |
| 126: | def response |
| 127: | @status = 404 if Worker.new('in',@r[-1]).delete.nil? |
| 128: | end |
| 129: | end #}}} |
| 130: | class OutDelete < Riddl::Implementation #{{{ |
| 131: | def response |
| 132: | @status = 404 if Worker.new('out',@r[-1]).delete.nil? |
| 133: | end |
| 134: | end #}}} |
| 135: | |
| 136: | class Command < Riddl::Implementation |
| 137: | ECMDS = %w{list send details delete answer upload help} |
| 138: | H_list = <<-end #{{{ |
| 139: | (NOTHING|in|out) |
| 140: | List all request and possible responses |
| 141: | end |
| 142: | #}}} |
| 143: | H_help = <<-end #{{{ |
| 144: | |
| 145: | Show this. |
| 146: | end |
| 147: | #}}} |
| 148: | H_send = <<-end #{{{ |
| 149: | INTEGER |
| 150: | Send one of the listed response messages to the correlation (PUSH). |
| 151: | end |
| 152: | #}}} |
| 153: | H_details = <<-end #{{{ |
| 154: | (NOTHING|in|out)? INTEGER |
| 155: | Send the contents of one of the listed request or response messages. If |
| 156: | second argument is omitted, details for request messages are shown. |
| 157: | Examples: |
| 158: | details 1 |
| 159: | details in 1 |
| 160: | details out 1 |
| 161: | end |
| 162: | #}}} |
| 163: | H_delete = <<-end #{{{ |
| 164: | (NOTHING|in|out)? INTEGER |
| 165: | Delete one or more of the request messages - this answers with an empty |
| 166: | response. |
| 167: | Examples: |
| 168: | delete 1 2 3 |
| 169: | delete in 1 2 3 |
| 170: | delete out 1 2 3 |
| 171: | end |
| 172: | #}}} |
| 173: | H_answer = <<-end #{{{ |
| 174: | INTEGER INTEGER |
| 175: | Answer a request (in) message (e.g. 1) with a response (out) message (e.g. |
| 176: | 2). The available messages are shown with list. The request (in) message is |
| 177: | then deleted. |
| 178: | Example: answer 1 2 |
| 179: | end |
| 180: | #}}} |
| 181: | H_upload = <<-end #{{{ |
| 182: | URL |
| 183: | store a new response message by loading it from a URL. |
| 184: | Example: |
| 185: | upload http://fp7-adventure.eu/components/response-messages/somemessage.xml |
| 186: | end |
| 187: | #}}} |
| 188: | |
| 189: | def response |
| 190: | cmd = @p.shift.value |
| 191: | para = @p.map{|x|x.value} |
| 192: | res = case cmd |
| 193: | when 'commands' |
| 194: | ECMDS |
| 195: | when 'help' |
| 196: | ECMDS.flatten.map do |m| |
| 197: | h = self.class.const_defined?("H_#{m}".to_sym) ? self.class.const_get("H_#{m}".to_sym) : "\n" |
| 198: | "#{m.to_s.red.bold} #{h}" |
| 199: | end.join |
| 200: | when 'list' |
| 201: | Worker.new(*para).list |
| 202: | when 'details' |
| 203: | Worker.new(*para).details |
| 204: | when 'send' |
| 205: | return nil if para.length != 1 |
| 206: | n2 = File.dirname(__FILE__) + "/out/#{para[0].to_i}/xml" |
| 207: | if para.length == 1 && File.exists?(n2) |
| 208: | data = File.read(n2) |
| 209: | client = Riddl::Client.interface("xmpp://[email protected]", "callback.xml", :xmpp => @env['xmpp']) |
| 210: | client.put Riddl::Parameter::Complex.new("gatewayResponse","text/xml",data) |
| 211: | end |
| 212: | nil |
| 213: | when 'delete' |
| 214: | Worker.new(*para).delete |
| 215: | when 'upload' |
| 216: | return nil if para.length != 1 |
| 217: | status, res = Riddl::Client.new(para[0]).get |
| 218: | if status == 200 |
| 219: | fil = res[0].value.read |
| 220: | success = begin |
| 221: | XML::Smart::string(fil).validate_against(XML::Smart::open_unprotected(File.dirname(__FILE__) + "/rngs/response.rng")) |
| 222: | rescue |
| 223: | false |
| 224: | end |
| 225: | if success |
| 226: | begin |
| 227: | id = id.nil? ? 0 : id + 1 |
| 228: | Dir.mkdir(File.dirname(__FILE__) + "/out/#{id}") |
| 229: | rescue SystemCallError |
| 230: | retry |
| 231: | end |
| 232: | File.write(File.dirname(__FILE__) + "/out/#{id}/xml",fil) |
| 233: | nil |
| 234: | else |
| 235: | "not a valid response message" |
| 236: | end |
| 237: | else |
| 238: | "not found" |
| 239: | end |
| 240: | when 'answer' |
| 241: | n1 = File.dirname(__FILE__) + "/in/#{para[0].to_i}/meta" |
| 242: | n2 = File.dirname(__FILE__) + "/out/#{para[1].to_i}/xml" |
| 243: | if para.length == 2 && File.exists?(n1) && File.exists?(n2) |
| 244: | to = JSON.parse(File.read(n1)) |
| 245: | data = File.read(n2) |
| 246: | client = Riddl::Client.interface("xmpp://" + to['answer'], "callback.xml", :xmpp => @env['xmpp']) |
| 247: | client.put Riddl::Parameter::Complex.new("gatewayResponse","text/xml",data) |
| 248: | doc = XML::Smart.string(data) |
| 249: | doc.register_namespace 'g', 'http://www.fp7-adventure.eu/xmlSchema/Gateways/' |
| 250: | Worker.new('in',para[0]).delete if doc.find('string(//g:header/g:commandSequence)') == 'LAST' |
| 251: | end |
| 252: | nil |
| 253: | else |
| 254: | "that's not a valid command" |
| 255: | end || [] |
| 256: | res = [res] unless res.is_a?(Hash) || res.is_a?(Array) |
| 257: | Riddl::Parameter::Complex.new("result","application/json",JSON::generate(res)) |
| 258: | end |
| 259: | end |
| 260: | |
| 261: | Riddl::Server.new(File.dirname(__FILE__) + '/gateway.xml', :port => 9300) do |
| 262: | xmpp '[email protected]', 'adventure_gateway_uvi' |
| 263: | accessible_description true |
| 264: | cross_site_xhr true |
| 265: | |
| 266: | on resource do |
| 267: | run Save if post 'gateway-request' |
| 268: | run Command if put 'command' |
| 269: | on resource 'in' do |
| 270: | run InList if get |
| 271: | on resource do |
| 272: | run InDetails if get |
| 273: | run InDelete if delete |
| 274: | end |
| 275: | end |
| 276: | on resource 'out' do |
| 277: | run OutList if get |
| 278: | on resource do |
| 279: | run OutDetails if get |
| 280: | run OutDelete if delete |
| 281: | end |
| 282: | end |
| 283: | end |
| 284: | end.loop! |
