/*
 * JBoss, the OpenSource J2EE webOS
 *
 * Distributable under LGPL license.
 * See terms of license at gnu.org.
 */

// $Id: ServiceReferenceable.java,v 1.6.2.3 2004/12/01 22:19:43 tdiesler Exp $
package org.jboss.webservice.client;

// $Id: ServiceReferenceable.java,v 1.6.2.3 2004/12/01 22:19:43 tdiesler Exp $

import org.jboss.deployment.DeploymentInfo;
import org.jboss.mx.util.MBeanServerLocator;
import org.jboss.webservice.AxisServiceMBean;
import org.jboss.webservice.metadata.PortComponentRefMetaData;
import org.jboss.webservice.metadata.ServiceRefMetaData;

import javax.management.MBeanServer;
import javax.naming.BinaryRefAddr;
import javax.naming.NamingException;
import javax.naming.Reference;
import javax.naming.Referenceable;
import javax.naming.StringRefAddr;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.net.URL;

/**
 * A JNDI reference to a javax.xml.rpc.Service
 * <p/>
 * It holds the information to reconstrut the javax.xml.rpc.Service
 * when the client does a JNDI lookup.
 *
 * @author Thomas.Diesler@jboss.org
 * @since 15-April-2004
 */
public class ServiceReferenceable implements Referenceable
{
   public static final String SERVICE_REF_META_DATA = "SERVICE_REF_META_DATA";
   public static final String DEPLOYMENT_URL = "DEPLOYMENT_URL";
   public static final String PORT_COMPONENT_LINK = "PORT_COMPONENT_LINK";
   public static final String PORT_COMPONENT_LINK_SERVLET = "PORT_COMPONENT_LINK_SERVLET";

   // The service-ref meta data
   private ServiceRefMetaData refMetaData;
   // The deployment URL of the web service client deployment
   private DeploymentInfo di;

   /**
    * A service referenceable for a WSDL document that is part of the deployment
    *
    * @param refMetaData The service-ref meta data
    * @param di          The client DeploymentInfo
    */
   public ServiceReferenceable(ServiceRefMetaData refMetaData, DeploymentInfo di)
   {
      this.refMetaData = refMetaData;
      this.di = di;
   }

   /**
    * Retrieves the Reference of this object.
    *
    * @return The non-null Reference of this object.
    * @throws NamingException If a naming exception was encountered while retrieving the reference.
    */
   public Reference getReference() throws NamingException
   {
      Reference myRef = new Reference(ServiceReferenceable.class.getName(), ServiceObjectFactory.class.getName(), null);

      // Add a reference to the client deployment URL
      URL deploymentURL = (di.localUrl != null ? di.localUrl : di.url);
      myRef.add(new StringRefAddr(DEPLOYMENT_URL, deploymentURL.toExternalForm()));

      // Add a reference to the ServiceRefMetaData
      myRef.add(new BinaryRefAddr(SERVICE_REF_META_DATA, marshallServiceRef()));

      // Add references to port component links
      PortComponentRefMetaData[] pcrArr = refMetaData.getPortComponentRefs();
      for (int i = 0; i < pcrArr.length; i++)
      {
         PortComponentRefMetaData pcr = pcrArr[i];
         String pcLink = pcr.getPortComponentLink();
         if (pcLink != null)
         {
            String deploymentName = di.getCanonicalName();

            int hashIndex = pcLink.indexOf("#");
            if (hashIndex > 0)
            {
               deploymentName = pcLink.substring(0, hashIndex);
               pcLink = pcLink.substring(hashIndex + 1);
               while (deploymentName.startsWith(".") || deploymentName.startsWith("/"))
                  deploymentName = deploymentName.substring(1);
            }

            String serviceID = deploymentName + "#" + pcLink;
            myRef.add(new StringRefAddr(PORT_COMPONENT_LINK, serviceID));

            try
            {
               MBeanServer server = MBeanServerLocator.locateJBoss();
               String host = (String)server.getAttribute(AxisServiceMBean.OBJECT_NAME, "WebServiceHost");
               int port = ((Integer)server.getAttribute(AxisServiceMBean.OBJECT_NAME, "WebServicePort")).intValue();

               String servletURL = "http://" + host + ":" + port + "/ws4ee/pclink";
               myRef.add(new StringRefAddr(PORT_COMPONENT_LINK_SERVLET, servletURL));
            }
            catch (Exception e)
            {
               throw new NamingException("Cannot obtain path to PortComponentLinkServlet, cause is " + e);
            }
         }
      }

      return myRef;
   }

   /**
    * Marshall the ServiceRefMetaData to an byte array
    */
   private byte[] marshallServiceRef() throws NamingException
   {
      // marshall_01 the ServiceRefMetaData
      ByteArrayOutputStream baos = new ByteArrayOutputStream(512);
      try
      {
         ObjectOutputStream oos = new ObjectOutputStream(baos);
         oos.writeObject(refMetaData);
         oos.close();
      }
      catch (IOException e)
      {
         throw new NamingException("Cannot marshall_01 service ref meta data, cause: " + e.toString());
      }
      return baos.toByteArray();
   }
}