/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.foo.log;

import org.osgi.framework.*;
import org.osgi.service.log.LogService;

/**
 * Broken code example showing single lookup of a service at bundle startup. The
 * client can now tell when the service is removed, but it doesn't automatically
 * switch to other LogService implementations after the service has disappeared.
 * 
 * To run this example:
 * 
 *   ./chapter04/dynamics/PICK_EXAMPLE 2
 * 
 * Expected result:
 * 
 *   <3> thread="LogService Tester", bundle=2 : logging ON
 *   <3> thread="LogService Tester", bundle=2 : ping
 *   ...
 * 
 * If you stop the simple LogService with "stop 1" you should see the following:
 * 
 *   <3> thread="Thread-1", bundle=2 : logging OFF
 *   <--> thread="LogService Tester", bundle=2 : LogService has gone
 *   ...
 * 
 * Which shows the client bundle knows the LogService it was using has now gone.
 * 
 * Restarting the simple LogService with "start 1" should have no affect, it is
 * only when you restart the client with "stop 2" and "start 2" that it repeats
 * the query in the start method and finds the new service:
 * 
 *   <5> thread="LogService Tester", bundle=2 : logging ON
 *   <5> thread="LogService Tester", bundle=2 : ping
 *   ...
 * 
 * Note that the new LogService has an increased "service.id" property of 5.
 **/
public class Activator implements BundleActivator {

  ServiceReference m_logServiceRef;

  BundleContext m_context;

  /**
   * START LOG TEST
   **/
  public void start(BundleContext context) {

    // this time we store the indirect service reference in the field instead of the instance
    m_logServiceRef = context.getServiceReference(LogService.class.getName());

    // we also need to remember our context so that we can dereference the handle later on
    m_context = context;

    // start new thread to test LogService - remember to keep bundle activator methods short!
    startTestThread();
  }

  /**
   * STOP LOG TEST
   **/
  public void stop(BundleContext context) {

    stopTestThread();
  }

  // Test LogService by periodically sending a message
  class LogServiceTest implements Runnable {
    public void run() {

      while (Thread.currentThread() == m_logTestThread) {

        // we use the saved bundle context and service reference to get the real instance
        LogService logService = (LogService) m_context.getService(m_logServiceRef);

        // if the dereferenced instance is null then we know the service has been removed
        if (logService != null) {
          logService.log(LogService.LOG_INFO, "ping");
        } else {
          alternativeLog("LogService has gone");
        }

        pauseTestThread();
      }
    }
  }

  //------------------------------------------------------------------------------------------
  //  The rest of this is just support code, not meant to show any particular best practices
  //------------------------------------------------------------------------------------------

  volatile Thread m_logTestThread;

  void startTestThread() {
    // start separate worker thread to run the actual tests, managed by the bundle lifecycle
    m_logTestThread = new Thread(new LogServiceTest(), "LogService Tester");
    m_logTestThread.setDaemon(true);
    m_logTestThread.start();
  }

  void stopTestThread() {
    // thread should cooperatively shutdown on the next iteration, because field is now null
    Thread testThread = m_logTestThread;
    m_logTestThread = null;
    if (testThread != null) {
      testThread.interrupt();
      try {testThread.join();} catch (InterruptedException e) {}
    }
  }

  protected void pauseTestThread() {
    try {
      // sleep for a bit
      Thread.sleep(5000);
    } catch (InterruptedException e) {}
  }

  void alternativeLog(String message) {
    // this provides similar style debug logging output for when the LogService disappears
    String tid = "thread=\"" + Thread.currentThread().getName() + "\"";
    String bid = "bundle=" + m_context.getBundle().getBundleId();
    System.out.println("<--> " + tid + ", " + bid + " : " + message);
  }
}
