/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 1999 Bull S.A.
 * Contact: jonas-team@objectweb.org
 *
 * This library 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 2.1 of the License, or any later version.
 *
 * This library 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 GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 * USA
 * --------------------------------------------------------------------------
 * $Id$
 * --------------------------------------------------------------------------
 */
package org.objectweb.jonas.server;

// Java imports
import java.util.ArrayList;
import java.util.List;

import javax.management.JMException;
import javax.management.MBeanServer;
import javax.management.MBeanServerNotification;
import javax.management.Notification;
import javax.management.NotificationFilter;
import javax.management.NotificationListener;
import javax.management.ObjectName;

import org.objectweb.jonas.common.Log;
import org.objectweb.jonas.container.EJBService;
import org.objectweb.jonas.ear.EarService;
import org.objectweb.jonas.jmx.JmxService;
import org.objectweb.jonas.management.AttributeAddNotification;
import org.objectweb.jonas.management.AttributeRemoveNotification;
import org.objectweb.jonas.management.ListenerJavaBean;
import org.objectweb.jonas.management.ReconfigException;
import org.objectweb.jonas.management.j2eemanagement.J2EEManagedObject;
import org.objectweb.jonas.resource.ResourceService;
import org.objectweb.jonas.service.ServiceManager;
import org.objectweb.jonas.web.JWebContainerService;
import org.objectweb.jonas_timer.TimerEvent;
import org.objectweb.jonas_timer.TimerEventListener;
import org.objectweb.jonas_timer.TimerManager;
import org.objectweb.util.monolog.api.BasicLevel;
import org.objectweb.util.monolog.api.Logger;

/**
 * MBean class for Server management.
 * Two classes are used to provide all management services : J2EEServer and J2EEServerMBean.
 * This class provide the notification's process when add or remove resources.
 * @author Guillaume Riviere
 * @author Michel Bruno
 * <br>contributors Adriana Danes, Michel-Ange Anton
 *
 */
public class J2EEServer extends J2EEManagedObject implements TimerEventListener, NotificationListener {
    /**
     * object to manage
     */
    private Server server = null;
    private String serverName = null;
    private String serverVersion = null;
    private String serverVendor = null;
    private String protocols = null;
    private ArrayList deployedObjects = null;
    private ArrayList resources = null;
    private ArrayList javaVMs = null;
    private String versions = null;
    private String jonasBase = null;
    /**
     * monitoring objects
     */
    private boolean activated = true;

    private int sizeTableMeasures = 120;
    private int range = 10;
    private Long[] tableMeasures;
    private long maxtotalmemory;
    private TimerEvent mytimer = null;

    /**
     * Management logger
     */
    private static Logger mgtLogger = Log.getLogger(Log.JONAS_MANAGEMENT_PREFIX);
    /**
     * Management logger
     */
    private static Logger evtLogger = Log.getLogger(Log.JONAS_MANAGEMENT_EVENT_PREFIX);

    /**
     * Notifications variables
     */
    // sequence number needed when sending notifications
    private long sequenceNumber = 0;

    /**Support JMX notification
    /*-- temporary solution allowing to support one listener (the ManagementListener MBean)
     */
    private ListenerJavaBean myListener = null;

    // Services used to deploy J2EE modules and applications
    /**
     * EJB Container service
     */
    private EJBService ejbService = null;
    /**
     * EAR service
     */
    private EarService earService = null;
    /**
     * Resource service
     */
    private ResourceService rarService = null;
    /**
     * War service
     */
    private JWebContainerService warService = null;

    private MBeanServer jmxServer = null;

    /**
     * MBean constructor
     * @param objectName object name of the managed object
     * @param server Server object
     * @param serverName server name
     * @param serverVersion JOnAS version
     * @param serverVendor is JOnAS
     * @param protocols protocols supported
     */
    public J2EEServer(String objectName, Server server, String serverName, String serverVersion
        , String serverVendor, String protocols, String versions) {
        super(objectName);
        this.server = server;
        this.serverName = serverName;
        this.serverVersion = serverVersion;
        this.serverVendor = serverVendor;
        this.protocols = protocols;
        this.versions = versions;
        initTable();
        deployedObjects = new ArrayList();
        resources = new ArrayList();
        javaVMs = new ArrayList();
        //Launch Timer
        mytimer = TimerManager.getInstance().addTimerMs(this, range * 1000, new Integer(1), true);
        // Get services references
        // In case one of these services is not running, a ServiceException is thrown
        // No exception treatement is provided, only keep reference null
        try {
            ejbService = (EJBService) ServiceManager.getInstance().getEjbService();
        } catch (Exception e) {
        }
        try {
            earService = (EarService) ServiceManager.getInstance().getEarService();
        } catch (Exception e) {
        }
        try {
            rarService = (ResourceService) ServiceManager.getInstance().getRarService();
        } catch (Exception e) {
        }
        try {
            warService = (JWebContainerService) ServiceManager.getInstance().getWebContainerService();
        } catch (Exception e) {
        }
        try {
            JmxService jmxService = (JmxService) ServiceManager.getInstance().getJmxService();
            jmxServer = jmxService.getJmxServer();
        } catch (Exception e) {
        }
        // Add myself as listner to  MBeanServerNotifications. Use for this the MBeanServerDelegate ObjectName as
        // argument in the addNotificationListener method (see JMX API for more info).
        // Use null NotificationFilter and null handback object.
        try {
            ObjectName delegate = new ObjectName("JMImplementation:type=MBeanServerDelegate");
            jmxServer.addNotificationListener(delegate, this, null, null);
        } catch (JMException me) {
            // MalformedObjectNameException should not occur if the JMX implementation is correct
            // InstanceNotFoundException should not occur as we use MBeanServerDelegate ObjectName
            throw new ReconfigException("ReconfigManager can't listen to MBeanServerNotifications because of exception: " + me.toString());
        }
    }

    /**
     * Server name
     * @return String JOnAS server name
     */
    public String getServerName() {
        return serverName;
    }

    /**
     * Server version
     * @return String JOnAS server version
     */
    public String getServerVersion() {
        return serverVersion;
    }

    /**
     * Server vendor
     * @return String JOnAS server version
     */
    public String getServerVendor() {
        return serverVendor;
    }

    /**
         * Return the list of MBean names corresponding to the deployed J2EEModules and J2EEApplications
     * @return java.util.ArrayList list of the deployed objects' MBean names
     */
    public List getDeployedObjects() {
        return deployedObjects;
    }

    /**
     * Add an object name to the <code>deployedObjects</code> list.
     * @param objectName Object name corresponding to a J2EEDeployedObject MBean
     */
    public void addDeployedObject(String objectName) {
        deployedObjects.add(objectName);
    }

    /**
     * Remove an object name to the <code>deployedObjects</code> list.
     * @param objectName Object name corresponding to a J2EEDeployedObject MBean
     */
    public void removeDeployedObject(String objectName) {
        deployedObjects.remove(objectName);
    }

    /**
     * Return the list of MBean names corresponding to the resources available on this server
     * @return java.util.ArrayList list of the resources' MBean names
     */
    public List getResources() {
        return resources;
    }

    /**
         * Return the list of MBean names corresponding to the JVMs on which this server has running threads
     * @return java.util.ArrayList list of the JVMs' MBean names
     */
    public List getJavaVMs() {
        return javaVMs;
    }

    /**
     * Add an object name to the <code>javaVMs</code> list.
     * @param objectName Object name correspondig to a JVM MBean
     */
    public void addJavaVM(String objectName) {
        javaVMs.add(objectName);
    }

    /**
     * Protocols supported by this Server
     * @return String protocols supported
     */
    public String getProtocols() {
        return protocols;
    }

    /**
     * Stop server (with stopping the JVM)
     */
    public void stop()
        throws Exception {
        server.stop();
    }

    /**
     * run the garbage collector
     */
    public void runGC() {
        Runtime.getRuntime().gc();
    }

    /**
     * get jvm used memory
     * @return Long - current used memory
     */
    public long getCurrentUsedMemory() {
        return usedMemory();
    }

    /**
     * get jvm total memory
     * @return Long - current total memory
     */
    public long getCurrentTotalMemory() {
        return totalMemory();
    }

    /**
     * set range
     * @param range range for free memory measurement
     */
    public void setRange(int range) {
        if (this.range != range) {
            synchronized (this) {
                if (range < 10) {
                    throw new IllegalArgumentException("range could not be < 10");
                }
                this.range = range;
                // reset table values
                initTable();
                /// change range
                mytimer.change(this.range * 1000, new Integer(1));
            }
        }
    }

    /**
     * get range
     * @return Integer - range
     */
    public int getRange() {
        return range;
    }

    /**
     * Set the size of the table of measures
     * @param sizeMeasuresTable Number of measures
     */
    public void setSizeTableMeasures(int sizeMeasuresTable) {
        if (sizeMeasuresTable != this.sizeTableMeasures) {
            synchronized (this) {
                if (sizeMeasuresTable <= 1) {
                    throw new IllegalArgumentException("number of measures could not be <= 1");
                }
                this.sizeTableMeasures = sizeMeasuresTable;
                initTable();
            }
        }
    }

    /**
     * Get the size of the table of measures
     * @return Number of measures
     */
    public int getSizeTableMeasures() {
        return sizeTableMeasures;
    }

    /**
     * get the table of value
     * @return Long[] measures table
     */
    public Long[] getTableMeasures() {
        return tableMeasures;
    }

    // ===============================================================
    // TimerEventListener implementation
    // ===============================================================
    /**
     * The measures timeout has expired
     * Do not synchronize this method to avoid deadlocks!
     * @param arg Object
     */
    public void timeoutExpired(Object arg) {
        int argvalue = ((Integer) arg).intValue();
        switch (argvalue) {
            case 1:

                //timeout expired
                long uvalue = usedMemory();
                long tmvalue = totalMemory();

                // put the max total memory
                if (tmvalue > maxtotalmemory) {
                    maxtotalmemory = tmvalue;
                }
                for (int i = 0; i < tableMeasures.length; i++) {
                    if (i != tableMeasures.length - 1) {
                        tableMeasures[i] = tableMeasures[i + 1];
                    } else {
                        tableMeasures[i] = new Long(uvalue);
                    }
                }
                break;
            default:

                //timeoutExpired do nothing;
                break;
        }
    }

    /**
     * set monitoring activation
     * @param pActivated true is activated
     */
    public void setActivated(boolean pActivated) {
        if (pActivated) {
            if (!activated) {
                synchronized (this) {
                    activated = true;
                    initTable();
                }
                // launch timer
                mytimer = TimerManager.getInstance().addTimerMs(this, range * 1000, (Object) new Integer(1), true);
            }
        } else {
            if (activated) {
                synchronized (this) {
                    activated = false;
                    initTable();
                    // stop timer
                    mytimer.stop();
                    mytimer.unset();
                }
            }
        }
    }

    /**
     * get monitoring activation
     * @return Boolean is activated
     */

    public boolean isActivated() {
        return activated;
    }

    /**
     * private function for monitoring
     * @return long  used memory
     */

    private long usedMemory() {
        long result = -1;
        while (result < 0) {
            result = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
        }

        return result / 1000;
    }

    /**
     * private function for monitoring
     * @return long  total memory
     */

    private long totalMemory() {
        return Runtime.getRuntime().totalMemory() / 1000;
    }

    /**
     * private init table
     */

    private void initTable() {
        tableMeasures = new Long[sizeTableMeasures];
        // init max totalmemory
        maxtotalmemory = totalMemory();
        for (int i = 0; i < tableMeasures.length; i++) {
            tableMeasures[i] = new Long(0);
        }
        // init the first value
        tableMeasures[tableMeasures.length - 1] = new Long(usedMemory());
    }

    /**
     * Deploy a stand-alone J2EE module packaged in a JAR file
     * @param fileName the file name of the JAR to be be deployed.
     * @return The ObjectName of the MBean associated to the deployed module
     * @throws Exception Management operation could not be executed or failed
     */
    public String deployJar(String fileName) throws Exception {
        if (ejbService != null) {
            return ejbService.createContainer(fileName);
        } else {
            throw new Exception("Service ejb not started on " + serverName);
        }
    }

    /**
     * Test if the specified filename is already deployed or not.
     * @param fileName the name of the jar file.
     * @return true if the jar is deployed, otherwise false.
     * @throws Exception Management operation could not be executed
     */
    public Boolean isJarDeployed(String fileName) throws Exception {
        if (ejbService != null) {
            return ejbService.isJarDeployed(fileName);
        } else {
            throw new Exception("Service ejb not started on " + serverName);
        }
    }

    /**
     * Uneploy a stand-alone J2EE module packaged in a JAR file
     * @param fileName the file name of the JAR to be be undeployed.
     * @throws Exception Management operation could not be executed or failed
     */
    public void unDeployJar(String fileName) throws Exception {
        if (ejbService != null) {
            ejbService.removeContainer(fileName);
        } else {
            throw new Exception("Service ejb not started on " + serverName);
        }
    }

    /**
     * Test if the specified filename is already deployed or not.
     * @param fileName the name of the war file.
     * @return true if the war is deployed, otherwise false.
     * @throws Exception Management operation could not be executed
     */
    public Boolean isWarDeployed(String fileName) throws Exception {
        if (warService != null) {
            return new Boolean(warService.isWarLoaded(fileName));
        } else {
            throw new Exception("Service web not started on " + serverName);
        }
    }

    /**
     * Deploy a stand-alone J2EE module packaged in a WAR file
     * @param fileName the file name of the WAR to be be deployed.
     * @return The ObjectName of the MBean associated to the deployed module
     * @throws Exception Management operation could not be executed or failed
     */
    public void deployWar(String fileName) throws Exception {
        if (warService != null) {
            warService.registerWarMBean(fileName);
        } else {
            throw new Exception("Service web not started on " + serverName);
        }
    }


    /**
     * Uneploy a stand-alone J2EE module packaged in a WAR file
     * @param fileName the file name of the WAR to be be undeployed.
     * @throws Exception Management operation could not be executed or failed
     */
    public void unDeployWar(String fileName) throws Exception {
        if (warService != null) {
            warService.unRegisterWarMBean(fileName);
        } else {
            throw new Exception("Service web not started on " + serverName);
        }
    }
    /**
     * Deploy a J2EE application packaged in a EAR file
     * @param fileName the file name of the EAR to be be deployed.
     * @return The ObjectName of the MBean associated to the deployed J2EE Application
     * @throws Exception Management operation could not be executed or failed
     */
    public String deployEar(String fileName) throws Exception {
        if (earService != null) {
            return earService.deployEar(fileName);
        } else {
            throw new Exception("Service ear not started on " + serverName);
        }
    }

    /**
     * Test if the specified filename is already deployed or not. This
     * method is defined in the EarService interface.
     * @param fileName the name of the ear file.
     * @return true if the ear is deployed, else false.
     * @throws Exception Management operation could not be executed or failed
     */
    public Boolean isEarDeployed(String fileName) throws Exception {
        if (earService != null) {
            return earService.isEarDeployed(fileName);
        } else {
            throw new Exception("Service ear not started on " + serverName);
        }
    }

    /**
     * undeploy a J2EE application packaged in a EAR file
     * @param fileName the file name of the EAR to be be undeployed.
     * @throws Exception Management operation could not be executed or failed
     */
    public void unDeployEar(String fileName) throws Exception {
        if (earService != null) {
            earService.unDeployEar(fileName);
        } else {
            throw new Exception("Service ear not started on " + serverName);
        }
    }

    /**
     * Deploy a J2EE application packaged in a RAR file
     * @param fileName the file name of the RAR to be be deployed.
     * @return The ObjectName of the MBean associated to the deployed J2EE Application
     * @throws Exception Management operation could not be executed or failed
     */
    public String deployRar(String fileName) throws Exception {
        if (rarService != null) {
            return rarService.deployRar(fileName);
        } else {
            throw new Exception("Service resource not started on " + serverName);
        }
    }

    /**
     * Test if the specified filename is already deployed or not.
     * @param fileName the name of the rar file.
     * @return true if the rar is deployed, else false.
     * @throws Exception Management operation could not be executed or failed
     */
    public Boolean isRarDeployed(String fileName) throws Exception {
        if (rarService != null) {
            return rarService.isRarDeployed(fileName);
        } else {
            throw new Exception("Service resource not started on " + serverName);
        }
    }

    /**
     * undeploy a J2EE application packaged in a RAR file
     * @param fileName the file name of the RAR to be be undeployed.
     * @throws Exception Management operation could not be executed or failed
     */
    public void unDeployRar(String fileName) throws Exception {
        if (rarService != null) {
            rarService.unDeployRar(fileName);
        } else {
            throw new Exception("Service resource not started on " + serverName);
        }
    }

    /**
     * Add an object name to the <code>resources</code> list.
     * @param pObjectName Object name correspondig to a J2EEResource MBean
     */
    public void addResource(String pObjectName) {
        synchronized (resources) {
            if (resources.contains(pObjectName)) {
                if (mgtLogger.isLoggable(BasicLevel.DEBUG)) {
                    mgtLogger.log(BasicLevel.DEBUG
                        , "The object name: " + pObjectName + " is already in the resources list");
                }
            } else {
                // Add
                resources.add(pObjectName);
                // Send the notification
                AttributeAddNotification notification = new AttributeAddNotification(getObjectName()
                    , sequenceNumber++, System.currentTimeMillis(), "", "resource"
                    , pObjectName.toString());
                sendNotification(notification);
                if (evtLogger.isLoggable(BasicLevel.DEBUG)) {
                    evtLogger.log(BasicLevel.DEBUG
                        , "AttributeAddNotification emitted: resource added to the J2EEServer: "
                        + pObjectName.toString());
                }
            }
        }
    }

    /**
     * Remove an object name from the <code>resources</code> list.
     * @param pObjectName Object name correspondig to a J2EEResource MBean
     * @return Object name to the removed J2EEResource MBean
     */
    public String removeResource(String pObjectName) {
        String sRet = null;
        synchronized (resources) {
            int index = resources.indexOf(pObjectName);
            if (index > -1) {
                // Remove
                sRet = (String) resources.remove(index);
                // Send the notification
                AttributeRemoveNotification notification = new AttributeRemoveNotification(
                    getObjectName(), sequenceNumber++, System.currentTimeMillis(), "", "resource"
                    , pObjectName.toString());
                sendNotification(notification);
                if (evtLogger.isLoggable(BasicLevel.DEBUG)) {
                    evtLogger.log(BasicLevel.DEBUG
                        , "AttributeRemoveNotification emitted: resource removed from the J2EEServer: "
                        + pObjectName.toString());
                }
            }
        }
        return sRet;
    }

    /**
     * Send a notification to the listener.
     * @param notification The notification to send
     */
    public void sendNotification(Notification notification) {
        // if registered listener(s)
        if (myListener != null) {
            NotificationListener listener = myListener.getListener();
            Object handback = myListener.getHandback();
            listener.handleNotification(notification, handback);
            if (evtLogger.isLoggable(BasicLevel.DEBUG)) {
                evtLogger.log(BasicLevel.DEBUG, "Notification sent");
            }
        }
    }

    /**
     * Add a new listener.
     * @param listner Listener to notify
     * @param filter Notification filter
     * @param handback ??
     * @throws java.lang.IllegalArgumentException
     */
    public void addNotificationListener(NotificationListener listner, NotificationFilter filter
        , java.lang.Object handback)
        throws java.lang.IllegalArgumentException {
        // Create new listener
        ListenerJavaBean lmb = new ListenerJavaBean(listner, filter, handback);
        myListener = lmb;
        if (evtLogger.isLoggable(BasicLevel.DEBUG)) {
            evtLogger.log(BasicLevel.DEBUG
                , "Listener added for J2EEServer MBean: " + getObjectName().toString());
            if (handback != null) {
                evtLogger.log(BasicLevel.DEBUG, " - with handback: " + handback.toString());
            }
        }
    }
    /**
     * Treat the notifications emitted by the JMX server.
     * This method determines the type of the notification and calls the specific treatement.
     * @param notification received notification
     * @param handback received handback object
     */
    public void handleNotification(Notification notification, java.lang.Object handback) {
        if (notification instanceof MBeanServerNotification) {
            // This notification is sent by the jmx server.
            //
            // ObjectName of the MBean that caused the notification
            ObjectName causeObjectName = ((MBeanServerNotification) notification).getMBeanName();
            String causeJ2eeType = causeObjectName.getKeyProperty("j2eeType");
            if (causeJ2eeType != null) {
                // The MBean that caused the notification is a J2EEManagedObject
                // Check for server name being the current server name
                String serverName = causeObjectName.getKeyProperty("J2EEServer");
                if (serverName != null) {
                    if (!serverName.equals(getServerName())) {
                        return;
                    }
                } else {
                    return;
                }
                // Check for J2EEResource MBeans
                boolean j2eeResource = false;
                if (causeJ2eeType.equals("JavaMailResource")
                    || causeJ2eeType.equals("JNDIResource")
                    || causeJ2eeType.equals("JDBCResource")
                    || causeJ2eeType.equals("JMSResource")
                    || causeJ2eeType.equals("JTAResource")
                    || causeJ2eeType.equals("JCAResource")
                    || causeJ2eeType.equals("RMIIIOPResource")
                    || causeJ2eeType.equals("URLResource")) {
                    j2eeResource = true;
                }
                if (j2eeResource) {
                    String notificationType = notification.getType();
                    handleResourceNotification(causeObjectName, notificationType);
                }
                // Check for J2EEDeployedObjects
                boolean j2eeDeployed = false;
                if (causeJ2eeType.equals("J2EEApplication")
                    || causeJ2eeType.equals("AppClientModule")
                    || causeJ2eeType.equals("EJBModule")
                    || causeJ2eeType.equals("WebModule")
                    || causeJ2eeType.equals("ResourceAdapterModule")) {
                    j2eeDeployed = true;
                }
                if (j2eeDeployed) {
                    String notificationType = notification.getType();
                    handleDeployedNotification(causeObjectName, notificationType);
                }
            }
        }
    }
    /**
     * Treat the registration/unregistration of J2EEResources.
     * @param resourceObjectName object name of the J2EEResource MBean that caused the notification
     * @param notificationType may be a REGISTRATION_NOTIFICATION or an UNREGISTRATION_NOTIFICATION
     */
    private void handleResourceNotification(ObjectName resourceObjectName, String notificationType) {
        if (notificationType.equals(MBeanServerNotification.REGISTRATION_NOTIFICATION)) {
            addResource(resourceObjectName.toString());
            if (evtLogger.isLoggable(BasicLevel.DEBUG)) {
                evtLogger.log(BasicLevel.DEBUG, "Resource " + resourceObjectName.toString() + " added to J2EEServer " + getServerName());
            }
        } else if (notificationType.equals(MBeanServerNotification.UNREGISTRATION_NOTIFICATION)) {
            removeResource(resourceObjectName.toString());
            if (evtLogger.isLoggable(BasicLevel.DEBUG)) {
                evtLogger.log(BasicLevel.DEBUG, "Resource " + resourceObjectName.toString() + " removed from J2EEServer " + getServerName());
            }
        }
    }
    /**
     * Treat the registration/unregistration of J2EEDeployedObjects.
     * @param deployedObjectName object name of the J2EEDeployedObject MBean that caused the notification
     * @param notificationType may be a REGISTRATION_NOTIFICATION or an UNREGISTRATION_NOTIFICATION
     */
    private void handleDeployedNotification(ObjectName deployedObjectName, String notificationType) {
        if (notificationType.equals(MBeanServerNotification.REGISTRATION_NOTIFICATION)) {
            addDeployedObject(deployedObjectName.toString());
            if (evtLogger.isLoggable(BasicLevel.DEBUG)) {
                evtLogger.log(BasicLevel.DEBUG, "J2EEDeployedObject " + deployedObjectName.toString() + " added to J2EEServer " + getServerName());
            }
        } else if (notificationType.equals(MBeanServerNotification.UNREGISTRATION_NOTIFICATION)) {
            removeDeployedObject(deployedObjectName.toString());
            if (evtLogger.isLoggable(BasicLevel.DEBUG)) {
                evtLogger.log(BasicLevel.DEBUG, "J2EEDeployedObject " + deployedObjectName.toString() + " removed from J2EEServer " + getServerName());
            }
        }
    }

	/**
	 * @return Returns the versions.
	 */
	public String getVersions() {
		return versions;
	}

	public String getJonasBase() {
		return jonasBase;
	}

	public void setJonasBase(String jonasBase) {
		this.jonasBase = jonasBase;
	}
}
