org.scratch.microwebserver.MicrowebserverService.java Source code

Java tutorial

Introduction

Here is the source code for org.scratch.microwebserver.MicrowebserverService.java

Source

/**
 * Android Micro Web Server
 * Copyright (C) 2011  ScR4tCh
 * Contact scr4tch@scr4tch.org
    
 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
    
 * This program 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. See the Lesser GNU General Public License for more details.
    
 * You should have received a copy of the GNU Lesser General Public License along with this program; if not, see <http://www.gnu.org/licenses/>.
 */
package org.scratch.microwebserver;

import java.io.IOException;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.sql.SQLException;
import java.util.Enumeration;
import java.util.Vector;

import org.scratch.microwebserver.data.DBManager;
import org.scratch.microwebserver.data.DatabaseManagerException;
import org.scratch.microwebserver.http.MicroWebServer;
import org.scratch.microwebserver.http.MicroWebServerListener;
import org.scratch.microwebserver.http.WebService;
import org.scratch.microwebserver.http.WebServiceReply;
import org.scratch.microwebserver.http.WebServices;
import org.scratch.microwebserver.messagebinder.MessageData;
import org.scratch.microwebserver.messagebinder.MessageTypes;
import org.scratch.microwebserver.properties.PropertyNames;
import org.scratch.microwebserver.properties.ServerProperties;
import org.scratch.microwebserver.util.MimeDetector;

import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.PowerManager.WakeLock;
import android.support.v4.app.NotificationCompat;

public class MicrowebserverService extends Service implements MicroWebServerListener {
    //dispatcher binding
    private ServiceConnection dispatchConn;
    private Messenger disptachMessenger;

    // "tray" icon
    private NotificationManager mNotificationManager;
    private NotificationCompat.Builder notificationBuilder;
    private PendingIntent notificationIntent;
    private int APPICON_ID = 2112;

    private MicroWebServer server;
    private final IBinder binder = new MicrowebserverServiceBinder();

    private ConnectionChangeReceiver networkChangedReceiver;

    private Vector<ServiceListener> listeners = new Vector<ServiceListener>();

    private long startTime = 0;

    private Vector<String> listeningAdresses = new Vector<String>();
    private LogEntryAdapter lea;

    private WakeLock wakeLock;

    public class ConnectionChangeReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {

            if (server != null && server.isOnline()) {
                System.err.println("CONNECTION HAS CHANGED !");
                System.err.println("SERVER UP, REBIND PORTS !");

                listeningAdresses = new Vector<String>();
                Enumeration<NetworkInterface> en;

                try {
                    en = NetworkInterface.getNetworkInterfaces();

                    while (en.hasMoreElements()) {
                        NetworkInterface intf = en.nextElement();
                        for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr
                                .hasMoreElements();) {
                            InetAddress inetAddress = enumIpAddr.nextElement();
                            if (!inetAddress.isLoopbackAddress())
                                listeningAdresses.add(inetAddress.getHostAddress());
                        }
                    }

                    if (listeningAdresses.size() > 0) //should always be true !
                    {
                        try {
                            server.recreateSockets(listeningAdresses);
                        } catch (IOException e) {
                            //TODO: log !
                            e.printStackTrace();
                        }
                    } else {
                        System.err.println("NO ADDRESSES, REBIND FAILED !!!!");
                        //TODO: log !
                    }
                } catch (SocketException e1) {
                    e1.printStackTrace();
                    //TODO: log !
                }
            }

        }
    }

    protected class MicrowebserverServiceBinder extends Binder {
        public void startServer() {
            startUp(false);
            try {
                server = new MicroWebServer();
                server.addMicroWebServerListener(MicrowebserverService.this);

                server.setMimeDetector(new MimeDetector(getResources().openRawResource(R.raw.magic)));

                listeningAdresses.clear();

                try {
                    for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en
                            .hasMoreElements();) {
                        NetworkInterface intf = en.nextElement();
                        for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr
                                .hasMoreElements();) {
                            InetAddress inetAddress = enumIpAddr.nextElement();
                            if (!inetAddress.isLoopbackAddress()) {
                                listeningAdresses.add(inetAddress.getHostAddress());
                            }
                        }
                    }
                } catch (SocketException ex) {
                    if (server != null) {
                        MicrowebserverService.this.log(server.fetchPreLogs());
                        server.shutdown();
                    }

                    MicrowebserverService.this.log(System.currentTimeMillis(),
                            MicroWebServerListener.LOGLEVEL_ERROR, "", "",
                            "Failed to start server:\n" + ex.getMessage());
                }

                MicrowebserverService.this.log(server.fetchPreLogs());
                startUp(true);

                //wake lock  --- configurable ?
                wakeLock = ((PowerManager) getSystemService(Context.POWER_SERVICE))
                        .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "microwebserver wakelock");
                wakeLock.acquire();

                //aquire wifi lock (if configured)

                createNotification("serving ...");
            } catch (Exception e) {
                if (server != null) {
                    MicrowebserverService.this.log(server.fetchPreLogs());
                    stopServer();
                    //server.shutdown();
                }

                MicrowebserverService.this.log(System.currentTimeMillis(), MicroWebServerListener.LOGLEVEL_ERROR,
                        "", "", "Failed to start server:\n" + e.getMessage());
                shutDown(true);
            }
        }

        public void stopServer() {
            if (server != null) {
                server.shutdown();

                try {
                    DBManager.getInstance(
                            ServerProperties.getInstance().getString(PropertyNames.DATABASES_PATH.toString()))
                            .close();
                } catch (SQLException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (DatabaseManagerException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }

                server.removeMicroWebServerListener(MicrowebserverService.this);

                if (wakeLock.isHeld())
                    wakeLock.release();
            }

            mNotificationManager.cancel(APPICON_ID);
        }

        //TODO: Test behavior !
        public void restartServer() {
            stopServer();
            startServer();
        }

        public Vector<String> getListeningAdresses() {
            return listeningAdresses;
        }

        public boolean isServerUp() {
            if (server != null)
                return server.isOnline();

            return false;
        }

        public void registerServiceListener(ServiceListener sl) {
            listeners.add(sl);
        }

        public void unregisterServiceListener(ServiceListener sl) {
            listeners.remove(sl);
        }

        public long getServerStartTime() {
            return MicrowebserverService.this.startTime;
        }

        public LogEntryAdapter getLogEntryAdapter() {
            return lea;
        }

        public void log(long t, int lev, String msg) {
            MicrowebserverService.this.log(t, lev, "", "", msg);
        }

        public Vector<RemoteWebService> getRegisteredRemoteWebServices() {
            return remoteServices;
        }
    }

    private void createNotification(final String msg) {
        Context ctx = this.getApplicationContext();
        Intent intent = new Intent(ctx, MicrowebserverActivity.class);
        intent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK);

        //Intent nIntent=new Intent(getBaseContext(),MicrowebserverActivity.class);
        //nIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);

        //notificationIntent=PendingIntent.getActivity(this,0,nIntent,PendingIntent.FLAG_UPDATE_CURRENT);
        notificationIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
        notificationBuilder.setTicker("service started").setSmallIcon(R.drawable.ic_launcher).setOngoing(true)
                .setContentTitle("MicroWebServer").setContentText(msg).setContentIntent(notificationIntent);

        mNotificationManager.notify(APPICON_ID, notificationBuilder.getNotification());
    }

    public void onCreate() {
        super.onCreate();

        connectDispatcher();

        networkChangedReceiver = new ConnectionChangeReceiver();
        IntentFilter filter = new IntentFilter();
        filter.addAction(android.net.ConnectivityManager.CONNECTIVITY_ACTION);

        registerReceiver(networkChangedReceiver, filter);

        startTime = System.currentTimeMillis();

        mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        notificationBuilder = new NotificationCompat.Builder(this);
        notificationBuilder.setOngoing(true);
        notificationBuilder.setAutoCancel(false);

        lea = new LogEntryAdapter(startTime, 86400000); //24h history

        startForeground(APPICON_ID, notificationBuilder.getNotification());

        log(System.currentTimeMillis(), LOGLEVEL_INFO, "", "", "Service started");
    }

    public void connectDispatcher() {
        final Intent i = new Intent("org.scratch.microwebserver");
        i.setComponent(new ComponentName("org.scratch.microwebserver",
                "org.scratch.microwebserver.RemoteDispatcherService"));

        dispatchConn = new ServiceConnection() {
            public void onServiceConnected(ComponentName className, IBinder service) {
                // This is called when the connection with the service has been
                // established, giving us the service object we can use to
                // interact with the service.  We are communicating with our
                // service through an IDL interface, so get a client-side
                // representation of that from the raw service object.
                disptachMessenger = new Messenger(service);

                // We want to monitor the service for as long as we are
                // connected to it.
                try {
                    System.err.println("SEND SERVER HELLO");

                    Message msg = new Message();
                    msg.what = MessageTypes.MSG_SERVER_HELLO.ordinal();
                    msg.replyTo = mMessenger;

                    disptachMessenger.send(msg);

                } catch (RemoteException e) {
                    // In this case the service has crashed before we could even
                    // do anything with it; we can count on soon being
                    // disconnected (and then reconnected if it can be restarted)
                    // so there is no need to do anything here.
                    //HANDLE !!!
                    e.printStackTrace();
                }

                // As part of the sample, tell the user what happened.
                //Toast.makeText(Binding.this, "remote service connected",Toast.LENGTH_SHORT).show();
            }

            public void onServiceDisconnected(ComponentName className) {
                // This is called when the connection with the service has been
                // unexpectedly disconnected -- that is, its process crashed.
                disptachMessenger = null;

                // As part of the sample, tell the user what happened.
                //                Toast.makeText(Binding.this, R.string.remote_service_disconnected,
                //                        Toast.LENGTH_SHORT).show();
            }
        };

        bindService(i, dispatchConn, Context.BIND_AUTO_CREATE);
    }

    void disconnectDispatcher() {
        // If we have received the service, and hence registered with
        // it, then now is the time to unregister.
        if (dispatchConn != null) {
            try {
                Message msg = Message.obtain(null, MessageTypes.MSG_SERVER_BYE.ordinal());
                msg.replyTo = mMessenger;
                disptachMessenger.send(msg);
            } catch (RemoteException e) {
                //HANDLE !!!
                // There is nothing special we need to do if the service
                // has crashed.
            }

            // Detach our existing connection.
            unbindService(dispatchConn);
        }
    }

    public void onDestroy() {
        log(System.currentTimeMillis(), LOGLEVEL_INFO, "", "", "Service destroyed");
        unregisterReceiver(networkChangedReceiver);
        super.onDestroy();
    }

    @Override
    public IBinder onBind(Intent i) {
        return binder;
    }

    @Override
    public void log(long t, int lev, String client, String request, String msg) {
        LogEntry le = new LogEntry(t, lev, msg, request, client);
        for (int i = 0; i < listeners.size(); i++)
            listeners.elementAt(i).log(le);
    }

    public void log(Vector<LogEntry> vl) {
        for (int l = 0; l < vl.size(); l++) {
            LogEntry le = vl.elementAt(l);
            for (int i = 0; i < listeners.size(); i++)
                listeners.elementAt(i).log(le);
        }
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // We want this service to continue running until it is explicitly
        // stopped, so return sticky.
        return START_STICKY;
    }

    @Override
    public void startUp(boolean started) {
        log(System.currentTimeMillis(), LOGLEVEL_INFO, "", "", "Server startup");

        for (int i = 0; i < listeners.size(); i++)
            listeners.elementAt(i).startUp(started);
    }

    @Override
    public void shutDown(boolean shutDown) {
        log(System.currentTimeMillis(), LOGLEVEL_INFO, "", "", "Server shutdown");

        for (int i = 0; i < listeners.size(); i++)
            listeners.elementAt(i).shutDown(shutDown);

    }

    @Override
    public void recreate(boolean b, Vector<String> addr) {
        for (int i = 0; i < listeners.size(); i++)
            listeners.elementAt(i).recreate(b, addr);
    }

    @Override
    public void serviceAdded(WebService service) {
        for (int i = 0; i < listeners.size(); i++)
            listeners.elementAt(i).webServiceAdded(service);
    }

    //get dispatched messages from remote service
    //messenger

    private Vector<RemoteWebService> remoteServices = new Vector<RemoteWebService>();
    private final Messenger mMessenger = new Messenger(new IncomingHandler());

    class IncomingHandler extends Handler {
        private final Vector<RemoteServiceCall> resultsPending = new Vector<RemoteServiceCall>();

        @Override
        public void handleMessage(Message msg) {
            if (msg.what == MessageTypes.MSG_REGISTER_SERVICE.ordinal()) {

                /**
                   * The following attrs must be passed :
                   * 
                   * String[] input_mime - (accepted mimetypes)
                   * String output_mime  - (output mimetype)
                   * boolean postdata      - (is postdata accepted ?)
                   * String group_alias  - "servicegroup" alias i.e. http://<serveraddr>/group_alias/....
                   * String service_name - service name i.e. http://<serveraddr>/group_alias/servicename
                   */

                Bundle b = msg.getData();

                if (b.containsKey(MessageData.PACKAGE_NAME.getFieldName())
                        && b.containsKey(MessageData.INPUT_MIMETYPES.getFieldName())
                        && b.containsKey(MessageData.OUTPUT_MIMETYPE.getFieldName())
                        && b.containsKey(MessageData.SUPPORTED_METHODS.getFieldName())
                        && b.containsKey(MessageData.SERVICEGROUP_ALIAS.getFieldName())
                        && b.containsKey(MessageData.SERVICE_NAME.getFieldName())) {
                    String[] input_mime = b.getStringArray(MessageData.INPUT_MIMETYPES.getFieldName());
                    String output_mime = b.getString(MessageData.OUTPUT_MIMETYPE.getFieldName());
                    int methods = b.getInt(MessageData.SUPPORTED_METHODS.getFieldName());
                    String group_alias = b.getString(MessageData.SERVICEGROUP_ALIAS.getFieldName());
                    String service_name = b.getString(MessageData.SERVICE_NAME.getFieldName());
                    String packageName = b.getString(MessageData.PACKAGE_NAME.getFieldName());

                    RemoteWebService rws = new RemoteWebService(packageName, this, disptachMessenger, mMessenger,
                            input_mime, output_mime, methods, group_alias, service_name);

                    if (!remoteServices.contains(rws)) {
                        remoteServices.add(rws);
                    }

                    System.err.println("Added Remote Webservice: " + group_alias + "/" + service_name);
                    log(System.currentTimeMillis(), MicroWebServerListener.LOGLEVEL_NORMAL, "", "",
                            "Added Remote Webservice: " + group_alias + "/" + service_name);

                    serviceAdded(rws);

                    WebServices.getInstance().registerService(group_alias, rws.getUri(), rws);
                } else {
                    System.err
                            .println("Adding remote webservice failed [" + msg.replyTo.getClass().getName() + "]");
                    //handle ...
                    log(System.currentTimeMillis(), MicroWebServerListener.LOGLEVEL_WARN, "", "",
                            "Adding remote webservice failed [" + msg.replyTo.getClass().getName() + "]");
                }
            } else if (msg.what == MessageTypes.MSG_SERVICE_REPLY.ordinal()) {

                System.err.println("GOT REPLY :D, HEUREKA :D");

                //NOTE msg.arg1 needs to be set (time invoked in seconds) !!!
                //      msg.arg2 needs to be set (time finished in seconds) !!!
                for (int i = 0; i < resultsPending.size(); i++) {
                    if (resultsPending.elementAt(i).t == msg.arg1) {
                        System.err.println("FOUND PENDING RESULT");

                        Bundle data = msg.getData();

                        WebServiceReply wsr = new WebServiceReply();
                        wsr.setDate(msg.arg2 * 1000);
                        wsr.setData(data.getByteArray("result"));
                        wsr.setMime(resultsPending.elementAt(i).service.getMime());

                        System.err.println("SETTING REPLY");
                        resultsPending.elementAt(i).service.setReply(wsr);

                        synchronized (resultsPending.elementAt(i).service) {
                            resultsPending.elementAt(i).service.notify();
                        }

                        resultsPending.remove(i);
                    }
                }

            } else {
                super.handleMessage(msg);
            }
        }

        public void registerPendingReply(final RemoteServiceCall rsc) {
            resultsPending.add(rsc);
        }

        public void unregisterPendingReply(final RemoteServiceCall rsc) {
            resultsPending.remove(rsc);
        }

    }
}