/* * Copyright (c) 1998-2011 Caucho Technology -- all rights reserved * * This file is part of Resin(R) Open Source * * Each copy or derived work must preserve the copyright notice and this * notice unmodified. * * Resin Open Source is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Resin Open Source is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty * of NON-INFRINGEMENT. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with Resin Open Source; if not, write to the * * Free Software Foundation, Inc. * 59 Temple Place, Suite 330 * Boston, MA 02111-1307 USA * * @author Emil Ong */ package com.caucho.quercus.lib.bam; import java.io.Serializable; import java.util.ArrayList; import java.util.Iterator; import java.util.Locale; import java.util.logging.Logger; import com.caucho.bam.BamError; import com.caucho.bam.actor.ActorSender; import com.caucho.bam.actor.SimpleActorSender; import com.caucho.bam.stream.MessageStream; import com.caucho.bam.stream.NullActor; import com.caucho.bam.stream.NullMessageStream; import com.caucho.hemp.broker.HempBroker; import com.caucho.hmtp.HmtpClient; import com.caucho.quercus.annotation.ClassImplementation; import com.caucho.quercus.annotation.Optional; import com.caucho.quercus.env.BooleanValue; import com.caucho.quercus.env.ConstStringValue; import com.caucho.quercus.env.Env; import com.caucho.quercus.env.StringValue; import com.caucho.quercus.env.Value; import com.caucho.quercus.function.AbstractFunction; import com.caucho.quercus.module.AbstractQuercusModule; import com.caucho.util.L10N; import com.caucho.vfs.Path; import com.caucho.xmpp.im.ImMessage; import com.caucho.xmpp.im.ImPresence; import com.caucho.xmpp.im.RosterItem; import com.caucho.xmpp.im.RosterQuery; import com.caucho.xmpp.im.Text; /** * BAM functions */ @ClassImplementation public class BamModule extends AbstractQuercusModule { private static final Logger log = Logger.getLogger(BamModule.class.getName()); private static final L10N L = new L10N(BamModule.class); private static final StringValue PHP_SELF = new ConstStringValue("PHP_SELF"); private static final StringValue SERVER_NAME = new ConstStringValue("SERVER_NAME"); private static BamPhpActor getActor(Env env) { Value actorValue = env.getGlobalValue("_quercus_bam_actor"); if (actorValue != null && ! actorValue.isNull()) return (BamPhpActor) actorValue.toJavaObject(); return null; } private static ActorSender getActorClient(Env env) { ActorSender connection = (ActorSender) env.getSpecialValue("_quercus_bam_connection"); // create a connection lazily if (connection == null) { HempBroker broker = HempBroker.getCurrent(); String address = "php@" + env.getGlobalVar("_SERVER").get(SERVER_NAME); String resource = env.getGlobalVar("_SERVER").get(PHP_SELF).toString(); if (resource.indexOf('/') == 0) resource = resource.substring(1); NullActor stream = new NullActor(address, broker); connection = new SimpleActorSender(stream, broker, address, resource); env.addCleanup(new BamConnectionResource(connection)); env.setSpecialValue("_quercus_bam_connection", connection); } return connection; } private static BamPhpServiceManager getServiceManager(Env env) { Value managerValue = env.getGlobalValue("_quercus_bam_service_manager"); if (managerValue != null && ! managerValue.isNull()) return (BamPhpServiceManager) managerValue.toJavaObject(); return null; } private static MessageStream getBrokerStream(Env env) { BamPhpActor actor = getActor(env); if (actor != null) return actor.getBroker(); ActorSender connection = getActorClient(env); return connection.getBroker(); } private static String getAddress(Env env) { BamPhpActor actor = getActor(env); if (actor != null) return actor.getAddress(); ActorSender connection = getActorClient(env); return connection.getAddress(); } public static Value bam_login(Env env, String url, String username, String password) { BamPhpActor actor = getActor(env); if (actor != null) return env.error("bam_login not available from actor script"); HmtpClient client = null;//new HmtpClient(url); BamConnectionResource resource = new BamConnectionResource(client); env.addCleanup(resource); try { client.connect(username, password); } catch (Exception e) { e.printStackTrace(); return env.error("Unable to connect to BAM server", e); } env.setSpecialValue("_quercus_bam_connection", client); return BooleanValue.TRUE; } public static Value bam_service_exists(Env env, String address) { BamPhpServiceManager manager = getServiceManager(env); if (manager == null) return env.error("bam_service_exists must be called from " + "service manager script"); return BooleanValue.create(manager.hasChild(address)); } /** * Registers a "child" service that is represented by the given script. **/ public static Value bam_register_service(Env env, String address, String script) { BamPhpServiceManager manager = getServiceManager(env); if (manager == null) return env.error("bam_register_service must be called from " + "service manager script"); Path path = env.getSelfDirectory().lookup(script); if (! path.exists()) return env.error("script not found: " + script); BamPhpActor child = new BamPhpActor(); child.setAddress(address); child.setScript(path); // child.setBroker(manager.getBroker()); //InjectManager container = InjectManager.getCurrent(); //container.injectObject(child); manager.addChild(address, child); return BooleanValue.TRUE; } /** * Registers a "child" service that is represented by the given script. **/ public static Value bam_unregister_service(Env env, String address) { BamPhpServiceManager manager = getServiceManager(env); if (manager == null) return env.error("bam_unregister_service must be called from " + "service manager script"); BamPhpActor service = manager.removeChild(address); if (service == null) return BooleanValue.FALSE; // XXX: manager.getBroker().removeMailbox(service); return BooleanValue.TRUE; } public static Value bam_actor_exists(Env env, String address) { BamPhpActor actor = getActor(env); if (actor == null) return env.error("bam_actor_exists must be called from actor script"); return BooleanValue.create(actor.hasChild(address)); } /** * Registers a "child" actor that is represented by the given script. **/ public static Value bam_register_actor(Env env, String address, String script) { BamPhpActor actor = getActor(env); if (actor == null) return env.error("bam_register_actor must be called from actor script"); BamPhpActor child = new BamPhpActor(); child.setAddress(address); Path path = env.getSelfDirectory().lookup(script); if (! path.exists()) return env.error("script not found: " + script); child.setScript(path); //InjectManager container = InjectManager.getCurrent(); //container.injectObject(child); actor.addChild(address, child); return BooleanValue.TRUE; } public static String bam_my_address(Env env) { return getAddress(env); } // // Utilities // public static String bam_bare_address(Env env, String uri) { int slash = uri.indexOf('/'); if (slash < 0) return uri; return uri.substring(0, slash); } public static String bam_address_resource(Env env, String uri) { int slash = uri.indexOf('/'); if (slash < 0 || slash == uri.length() - 1) return ""; return uri.substring(slash + 1); } // // Transmit // public static void bam_send_message(Env env, String to, Serializable value) { getBrokerStream(env).message(to, getAddress(env), value); } public static void bam_send_message_error(Env env, String to, Serializable value, BamError error) { getBrokerStream(env).messageError(to, getAddress(env), value, error); } public static Value bam_send_query(Env env, long id, String to, Serializable value) { String from = getAddress(env); getBrokerStream(env).query(id, to, from, value); return BooleanValue.TRUE; } public static void bam_send_query_result(Env env, long id, String to, Serializable value) { getBrokerStream(env).queryResult(id, to, getAddress(env), value); } public static void bam_send_query_error(Env env, long id, String to, Serializable value, BamError error) { getBrokerStream(env).queryError(id, to, getAddress(env), value, error); } public static Value im_send_message(Env env, String to, String from, Value body, @Optional("chat") String type, @Optional Value subject, @Optional String thread, @Optional Serializable[] extras) { Text[] subjects = null; // extract subject text if (subject != null) { if (subject.isArray()) { subjects = new Text[subject.getSize()]; int i = 0; Iterator<Value> iterator = subject.getValueIterator(env); while (iterator.hasNext()) { Value subjectValue = iterator.next(); if (! subjectValue.isString()) return env.error("subject values must be strings"); subjects[i++] = new Text(subjectValue.toString()); } } else if (subject.isString()) { if (! subject.isString()) return env.error("subject values must be strings"); subjects = new Text[] { new Text(subject.toString()) }; } } // extract body text Text[] bodies = null; if (body.isArray()) { bodies = new Text[body.getSize()]; int i = 0; Iterator<Value> iterator = body.getValueIterator(env); while (iterator.hasNext()) { Value bodyValue = iterator.next(); if (! bodyValue.isString()) return env.error("body values must be strings"); bodies[i++] = new Text(bodyValue.toString()); } } else if (body.isString()) { if (! body.isString()) return env.error("body values must be strings"); bodies = new Text[] { new Text(body.toString()) }; } ImMessage message = new ImMessage(to, from, type, subjects, bodies, thread, extras); bam_send_message(env, to, message); return BooleanValue.TRUE; } public static RosterItem im_create_roster_item(Env env, String address, @Optional String name, @Optional String subscription, @Optional ArrayList<String> groupList) { if ("".equals(subscription)) subscription = "to"; return new RosterItem(null, address, name, subscription, groupList); } public static void im_send_roster(Env env, long id, String to, ArrayList<RosterItem> roster) { bam_send_query_result(env, id, to, new RosterQuery(roster)); } private static ImPresence createPresence(Env env, String to, String from, String show, String status, int priority, ArrayList<Serializable> extras) { if ("".equals(from)) from = getAddress(env); if ("".equals(show)) show = null; Text statusText = null; if (! "".equals(status)) statusText = new Text(status); return new ImPresence(to, from, show, statusText, priority, extras); } public static void im_send_presence(Env env, String to, @Optional String from, @Optional String show, @Optional String status, @Optional int priority, @Optional ArrayList<Serializable> extras) { ImPresence presence = createPresence(env, to, from, show, status, priority, extras); bam_send_message(env, to, presence); } public static void im_send_presence_unavailable(Env env, String to, @Optional String from, @Optional String show, @Optional String status, @Optional int priority, @Optional ArrayList<Serializable> extras) { // XXX: needs to be ImPresenceUnavailable ImPresence presence = createPresence(env, to, from, show, status, priority, extras); bam_send_message(env, to, presence); } /** * Makes a subscription request. **/ public static void im_send_presence_subscribe(Env env, String to, @Optional String from, @Optional String show, @Optional String status, @Optional int priority, @Optional ArrayList<Serializable> extras) { // XXX: presenceSubscribe ImPresence presence = createPresence(env, to, from, show, status, priority, extras); bam_send_message(env, to, presence); } /** * Approves a subscription request. **/ public static void im_send_presence_subscribed(Env env, String to, @Optional String from, @Optional String show, @Optional String status, @Optional int priority, @Optional ArrayList<Serializable> extras) { // presenceSubscribed ImPresence presence = createPresence(env, to, from, show, status, priority, extras); bam_send_message(env, to, presence); } /** * Makes an unsubscription request. **/ public static void im_send_presence_unsubscribe(Env env, String to, @Optional String from, @Optional String show, @Optional String status, @Optional int priority, @Optional ArrayList<Serializable> extras) { // XXX: presenceUnsubscribe ImPresence presence = createPresence(env, to, from, show, status, priority, extras); bam_send_message(env, to, presence); } /** * Rejects a subscription request. **/ public static void im_send_presence_unsubscribed(Env env, String to, @Optional String from, @Optional String show, @Optional String status, @Optional int priority, @Optional ArrayList<Serializable> extras) { // presenceUnsubscribe ImPresence presence = createPresence(env, to, from, show, status, priority, extras); bam_send_message(env, to, presence); } public static void im_send_presence_probe(Env env, String to, @Optional String from, @Optional String show, @Optional String status, @Optional int priority, @Optional ArrayList<Serializable> extras) { // presenceProbe ImPresence presence = createPresence(env, to, from, show, status, priority, extras); bam_send_message(env, to, presence); } /** * Dispatches messages, queries, and presences to handler functions based * on their prefixes. **/ public static Value bam_dispatch(Env env) { // manager script dispatch BamPhpServiceManager manager = getServiceManager(env); if (manager != null) { AbstractFunction function = null; if (env.getGlobalValue("_quercus_bam_start_service") != null) { function = env.findFunction("bam_start_service"); } else if (env.getGlobalValue("_quercus_bam_stop_service") != null) { function = env.findFunction("bam_stop_service"); } if (function == null) { env.setGlobalValue("_quercus_bam_function_return", BooleanValue.FALSE); return BooleanValue.FALSE; } Value address = env.getGlobalValue("_quercus_bam_service_address"); Value ret = function.call(env, address); env.setGlobalValue("_quercus_bam_function_return", ret); return BooleanValue.TRUE; } // actor script dispatch Value eventTypeValue = env.getGlobalValue("_quercus_bam_event_type"); if (eventTypeValue == null) return BooleanValue.FALSE; BamEventType eventType = (BamEventType) eventTypeValue.toJavaObject(); Value to = env.getGlobalValue("_quercus_bam_to"); Value from = env.getGlobalValue("_quercus_bam_from"); Value value = env.getGlobalValue("_quercus_bam_value"); AbstractFunction function = findFunction(env, eventType.getPrefix(), value); if (function == null) { log.fine(L.l("bam handler function not found for {0}", eventType)); return BooleanValue.FALSE; } Value functionReturn = BooleanValue.FALSE; if (eventType.hasId() && eventType.hasError()) { Value id = env.getGlobalValue("_quercus_bam_id"); Value error = env.getGlobalValue("_quercus_bam_error"); functionReturn = function.call(env, id, to, from, value, error); } else if (! eventType.hasId() && eventType.hasError()) { Value error = env.getGlobalValue("_quercus_bam_error"); functionReturn = function.call(env, to, from, value, error); } else if (eventType.hasId() && ! eventType.hasError()) { Value id = env.getGlobalValue("_quercus_bam_id"); functionReturn = function.call(env, id, to, from, value); } else { functionReturn = function.call(env, to, from, value); } env.setGlobalValue("_quercus_bam_function_return", functionReturn); return functionReturn; } /** * Finds the handler function for a value with the given prefix. If there * is a specific handler for a specific value type, that is returned * otherwise the generic handler (with the name of the prefix) is returned * if found. **/ private static AbstractFunction findFunction(Env env, String prefix, Value value) { if (value == null) return env.findFunction(prefix); Object obj = value.toJavaObject(); if (obj == null) return env.findFunction(prefix); String typeName = obj.getClass().getSimpleName().toLowerCase(Locale.ENGLISH); String functionName = prefix + '_' + typeName; AbstractFunction function = env.findFunction(functionName); if (function == null) function = env.findFunction(prefix); return function; } }