directory-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ersi...@apache.org
Subject svn commit: r555595 - in /directory/apacheds/trunk: core-unit/src/main/java/org/apache/directory/server/core/unit/ core-unit/src/test/java/org/apache/directory/server/core/trigger/ core/src/main/java/org/apache/directory/server/core/sp/ core/src/main/j...
Date Thu, 12 Jul 2007 11:46:10 GMT
Author: ersiner
Date: Thu Jul 12 04:46:08 2007
New Revision: 555595

URL: http://svn.apache.org/viewvc?view=rev&rev=555595
Log:
DIRSERVER-991 & DIRSERVER-992

Abstracted the SP execution framework from Trigger Service.
Replaced supported schema elements for Java Stored Procedures with new ones.

Added:
    directory/apacheds/trunk/core-unit/src/test/java/org/apache/directory/server/core/trigger/BackupUtilitiesSP.java
      - copied, changed from r555507, directory/apacheds/trunk/server-unit/src/test/java/org/apache/directory/server/BackupUtilitiesSP.java
    directory/apacheds/trunk/core-unit/src/test/java/org/apache/directory/server/core/trigger/ListUtilsSP.java
      - copied, changed from r555507, directory/apacheds/trunk/server-unit/src/test/java/org/apache/directory/server/ListUtilsSP.java
    directory/apacheds/trunk/core-unit/src/test/java/org/apache/directory/server/core/trigger/TriggerServiceITest.java
    directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/sp/JavaStoredProcEngine.java
    directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/sp/JavaStoredProcEngineConfig.java
    directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/sp/LdapJavaStoredProcClassLoader.java
    directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/sp/StoredProcEngine.java
    directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/sp/StoredProcEngineConfig.java
    directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/sp/StoredProcExecutionManager.java
    directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/sp/StoredProcUtils.java
    directory/apacheds/trunk/core/src/test/java/org/apache/directory/server/core/sp/
    directory/apacheds/trunk/core/src/test/java/org/apache/directory/server/core/sp/StoredProcUtilsTest.java
    directory/apacheds/trunk/server-unit/src/test/java/org/apache/directory/server/DITUtilitiesSP.java
    directory/apacheds/trunk/server-unit/src/test/java/org/apache/directory/server/HelloWorldProcedure.java
Removed:
    directory/apacheds/trunk/core-unit/src/test/java/org/apache/directory/server/core/trigger/AbstractTriggerServiceTest.java
    directory/apacheds/trunk/core-unit/src/test/java/org/apache/directory/server/core/trigger/DefaulTriggerServiceITest.java
    directory/apacheds/trunk/server-unit/src/test/java/org/apache/directory/server/BackupUtilitiesSP.java
    directory/apacheds/trunk/server-unit/src/test/java/org/apache/directory/server/ListUtilsSP.java
    directory/apacheds/trunk/server-unit/src/test/java/org/apache/directory/server/TriggerServiceTest.java
Modified:
    directory/apacheds/trunk/core-unit/src/main/java/org/apache/directory/server/core/unit/AbstractTestCase.java
    directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/trigger/TriggerService.java

Modified: directory/apacheds/trunk/core-unit/src/main/java/org/apache/directory/server/core/unit/AbstractTestCase.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/core-unit/src/main/java/org/apache/directory/server/core/unit/AbstractTestCase.java?view=diff&rev=555595&r1=555594&r2=555595
==============================================================================
--- directory/apacheds/trunk/core-unit/src/main/java/org/apache/directory/server/core/unit/AbstractTestCase.java
(original)
+++ directory/apacheds/trunk/core-unit/src/main/java/org/apache/directory/server/core/unit/AbstractTestCase.java
Thu Jul 12 04:46:08 2007
@@ -47,6 +47,7 @@
 import org.apache.directory.server.schema.registries.Registries;
 import org.apache.directory.shared.ldap.ldif.Entry;
 import org.apache.directory.shared.ldap.ldif.LdifReader;
+import org.apache.directory.shared.ldap.name.LdapDN;
 
 
 /**
@@ -392,5 +393,24 @@
     protected void setLoadClass( Class loadClass )
     {
         this.loadClass = loadClass;
+    }
+    
+    
+    /**
+     * Inject an ldif String into the server. DN must be relative to the
+     * root.
+     */
+    protected void injectEntries( String ldif ) throws NamingException
+    {
+        LdifReader reader = new LdifReader();
+        List<Entry> entries = reader.parseLdif( ldif );
+        
+        Iterator<Entry> entryIter = entries.iterator(); 
+        
+        while ( entryIter.hasNext() )
+        {
+            Entry entry = entryIter.next();
+            rootDSE.createSubcontext( new LdapDN( entry.getDn() ), entry.getAttributes()
);
+        }
     }
 }

Copied: directory/apacheds/trunk/core-unit/src/test/java/org/apache/directory/server/core/trigger/BackupUtilitiesSP.java
(from r555507, directory/apacheds/trunk/server-unit/src/test/java/org/apache/directory/server/BackupUtilitiesSP.java)
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/core-unit/src/test/java/org/apache/directory/server/core/trigger/BackupUtilitiesSP.java?view=diff&rev=555595&p1=directory/apacheds/trunk/server-unit/src/test/java/org/apache/directory/server/BackupUtilitiesSP.java&r1=555507&p2=directory/apacheds/trunk/core-unit/src/test/java/org/apache/directory/server/core/trigger/BackupUtilitiesSP.java&r2=555595
==============================================================================
--- directory/apacheds/trunk/server-unit/src/test/java/org/apache/directory/server/BackupUtilitiesSP.java
(original)
+++ directory/apacheds/trunk/core-unit/src/test/java/org/apache/directory/server/core/trigger/BackupUtilitiesSP.java
Thu Jul 12 04:46:08 2007
@@ -17,7 +17,7 @@
  *  under the License. 
  *  
  */
-package org.apache.directory.server;
+package org.apache.directory.server.core.trigger;
 
 import javax.naming.Name;
 import javax.naming.NamingException;

Copied: directory/apacheds/trunk/core-unit/src/test/java/org/apache/directory/server/core/trigger/ListUtilsSP.java
(from r555507, directory/apacheds/trunk/server-unit/src/test/java/org/apache/directory/server/ListUtilsSP.java)
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/core-unit/src/test/java/org/apache/directory/server/core/trigger/ListUtilsSP.java?view=diff&rev=555595&p1=directory/apacheds/trunk/server-unit/src/test/java/org/apache/directory/server/ListUtilsSP.java&r1=555507&p2=directory/apacheds/trunk/core-unit/src/test/java/org/apache/directory/server/core/trigger/ListUtilsSP.java&r2=555595
==============================================================================
--- directory/apacheds/trunk/server-unit/src/test/java/org/apache/directory/server/ListUtilsSP.java
(original)
+++ directory/apacheds/trunk/core-unit/src/test/java/org/apache/directory/server/core/trigger/ListUtilsSP.java
Thu Jul 12 04:46:08 2007
@@ -17,7 +17,7 @@
  *  under the License. 
  *  
  */
-package org.apache.directory.server;
+package org.apache.directory.server.core.trigger;
 
 import javax.naming.Name;
 import javax.naming.NamingException;

Added: directory/apacheds/trunk/core-unit/src/test/java/org/apache/directory/server/core/trigger/TriggerServiceITest.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/core-unit/src/test/java/org/apache/directory/server/core/trigger/TriggerServiceITest.java?view=auto&rev=555595
==============================================================================
--- directory/apacheds/trunk/core-unit/src/test/java/org/apache/directory/server/core/trigger/TriggerServiceITest.java
(added)
+++ directory/apacheds/trunk/core-unit/src/test/java/org/apache/directory/server/core/trigger/TriggerServiceITest.java
Thu Jul 12 04:46:08 2007
@@ -0,0 +1,238 @@
+/*
+ *  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.apache.directory.server.core.trigger;
+
+
+import javax.naming.NamingException;
+import javax.naming.directory.Attributes;
+import javax.naming.ldap.LdapContext;
+
+import org.apache.directory.server.core.unit.AbstractAdminTestCase;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.apache.directory.shared.ldap.sp.BaseJavaStoredProcUtils;
+import org.apache.directory.shared.ldap.trigger.TriggerUtils;
+import org.apache.directory.shared.ldap.util.AttributeUtils;
+
+
+/**
+ * Integration tests for TriggerService.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev:$
+ */
+public class TriggerServiceITest extends AbstractAdminTestCase
+{
+    private LdapContext ctx;
+    LdapContext spCtx;
+    
+    
+    public void setUp() throws Exception
+    {
+        super.setUp();
+
+        ctx = sysRoot;
+        Attributes spContainer = new AttributesImpl( "objectClass", "top", true );
+        spContainer.get( "objectClass" ).add( "organizationalUnit" );
+        spContainer.put( "ou", "Stored Procedures" );
+        spCtx = ( LdapContext ) ctx.createSubcontext( "ou=Stored Procedures", spContainer
);
+    }
+    
+    
+    public void tearDown() throws Exception
+    {
+        ctx.close();
+        ctx = null;
+        
+        super.tearDown();
+    }
+    
+    
+    public void testAfterDeleteBackupDeletedEntryEntryTrigger() throws NamingException
+    {
+        String ldif  = 
+            "version: 1\n" +
+            "\n" +
+            "dn: ou=backupContext, ou=system\n"+
+            "objectClass: top\n" +
+            "objectClass: organizationalUnit\n" +
+            "ou: backupContext\n" +
+            "\n" +
+            "dn: ou=testEntry, ou=system\n" +
+            "objectClass: top\n" +
+            "objectClass: organizationalUnit\n" +
+            "ou: testEntry\n";
+        
+        // Inject the ldif file into the server.
+        injectEntries( ldif );
+        
+        // Load the stored procedure unit which has the stored procedure to be triggered.
+        BaseJavaStoredProcUtils.loadStoredProcedureClass( spCtx, BackupUtilitiesSP.class
);
+        
+        // Create the Entry Trigger Specification.
+        TriggerUtils.defineTriggerExecutionSpecificPoint( ctx );
+        LdapContext entry = ( LdapContext ) ctx.lookup( "ou=testEntry" );
+        String triggerSpec = "AFTER Delete CALL \"" + BackupUtilitiesSP.class.getName() +
+            ":backupDeleted\" ( $ldapContext \"\", $name, $operationPrincipal, $deletedEntry
);";
+        TriggerUtils.loadEntryTriggerSpecification( entry, triggerSpec );
+        
+        // Delete the test entry in order to fire the Trigger.
+        sysRoot.destroySubcontext( "ou=testEntry" );
+        
+        // ------------------------------------------
+        // The trigger should be fired at this point.
+        // ------------------------------------------
+        
+        // Check if the Trigger really worked (backed up the deleted entry).
+        assertNotNull( sysRoot.lookup( "ou=testEntry,ou=backupContext" ) );
+    }
+    
+    
+    public void testAfterDeleteBackupDeletedEntryPrescriptiveTrigger() throws NamingException
+    {
+        // Load the stored procedure unit which has the stored procedure to be triggered.
+        BaseJavaStoredProcUtils.loadStoredProcedureClass( spCtx, BackupUtilitiesSP.class
);
+        
+        // Create a container for backing up deleted entries.
+        String ldif  = 
+            "version: 1\n" +
+            "\n" +
+            "dn: ou=backupContext, ou=system\n"+
+            "objectClass: top\n" +
+            "objectClass: organizationalUnit\n" +
+            "ou: backupContext\n";
+        
+        // Inject the ldif file into the server.
+        injectEntries( ldif );
+        
+        // Create the Trigger Specification within a Trigger Subentry.
+        TriggerUtils.defineTriggerExecutionSpecificPoint( ctx );
+        TriggerUtils.createTriggerExecutionSubentry( ctx,
+                                                     "triggerSubentry1",
+                                                     "{}",
+                                                     "AFTER Delete " +
+                                                         "CALL \"" + BackupUtilitiesSP.class.getName()
+ ":backupDeleted\" " +
+                                                             " ( $ldapContext \"\", $name,
$operationPrincipal, $deletedEntry );" );        
+
+        /**
+         * The Trigger Specification without Java clutter:
+         * 
+         * AFTER Delete
+         *     CALL "BackupUtilitiesSP.backupDeleted" ( $ldapContext "", $name, $operationPrincipal,
$deletedEntry );
+         * 
+         */   
+        
+        // Create a test entry which is selected by the Trigger Subentry.
+        String ldif2  = 
+            "version: 1\n" +
+            "\n" +
+            "dn: ou=testou, ou=system\n" +
+            "objectClass: top\n" +
+            "objectClass: organizationalUnit\n" +
+            "ou: testou\n";
+        
+        // Inject the ldif file into the server.
+        injectEntries( ldif2 );
+        
+        // Delete the test entry in order to fire the Trigger.
+        sysRoot.destroySubcontext( "ou=testou" );
+        
+        // ------------------------------------------
+        // The trigger should be fired at this point.
+        // ------------------------------------------
+        
+        // Check if the Trigger really worked (backed up the deleted entry).
+        assertNotNull( sysRoot.lookup( "ou=testou,ou=backupContext" ) );
+    }
+    
+    
+    public void testAfterAddSubscribeUserToSomeGroupsPrescriptiveTrigger() throws NamingException
+    {
+        // Create two groups to be subscribed to : staff and teachers.
+        String ldif  = 
+            "version: 1\n" +
+            "\n" +
+            "dn: cn=staff, ou=system\n"+
+            "objectClass: top\n" +
+            "objectClass: groupOfUniqueNames\n" +
+            "uniqueMember: cn=dummy\n"+
+            "cn: staff\n" +
+            "\n" +
+            "dn: cn=teachers, ou=system\n"+
+            "objectClass: top\n" +
+            "objectClass: groupOfUniqueNames\n" +
+            "uniqueMember: cn=dummy\n"+
+            "cn: teachers\n";
+        
+        // Load the stored procedure unit which has the stored procedure to be triggered.
+        BaseJavaStoredProcUtils.loadStoredProcedureClass( spCtx, ListUtilsSP.class );
+
+        // Inject the ldif file into the server
+        injectEntries( ldif );
+            
+        // Create the Trigger Specification within a Trigger Subentry.
+        String staffDN = "cn=staff, ou=system";
+        String teachersDN = "cn=teachers, ou=system";
+
+        
+        // Create the Triger Specification within a Trigger Subentry.
+        TriggerUtils.defineTriggerExecutionSpecificPoint( ctx );
+        TriggerUtils.createTriggerExecutionSubentry( ctx,
+                                                     "triggerSubentry1",
+                                                     "{}",
+                                                     "AFTER Add " +
+                                                         "CALL \"" + ListUtilsSP.class.getName()
+ ":subscribeToGroup\" ( $entry , $ldapContext \"" + staffDN + "\" ); " +
+                                                         "CALL \"" + ListUtilsSP.class.getName()
+ ":subscribeToGroup\" ( $entry , $ldapContext \"" + teachersDN + "\" );" );
+        
+        /**
+         * The Trigger Specification without Java clutter:
+         * 
+         * AFTER Add
+         *     CALL "ListUtilsSP:subscribeToGroup" ( $entry , $ldapContext "cn=staff, ou=system"
);
+         *     CALL "ListUtilsSP:subscribeToGroup" ( $entry , $ldapContext "cn=teachers,
ou=system" );
+         * 
+         */
+
+        // Create a test entry which is selected by the Trigger Subentry.
+        String testEntry  = 
+            "version: 1\n" +
+            "\n" +
+            "dn: cn=The Teacher of All Times, ou=system\n"+
+            "objectClass: top\n" +
+            "objectClass: inetOrgPerson\n" +
+            "cn: The Teacher of All Times\n" +
+            "sn: TheTeacher";
+
+        // Inject the entry into the server
+        injectEntries( testEntry );
+
+        // ------------------------------------------
+        // The trigger should be fired at this point.
+        // ------------------------------------------
+        
+        // Check if the Trigger really worked (subscribed the user to the groups).
+        Attributes staff = sysRoot.getAttributes( "cn=staff" );
+        Attributes teachers = sysRoot.getAttributes( "cn=teachers" );
+        String testEntryName = ( ( LdapContext )sysRoot.lookup( "cn=The Teacher of All Times"
) ).getNameInNamespace();
+        assertTrue( AttributeUtils.containsValueCaseIgnore( staff.get( "uniqueMember" ),
testEntryName ) );
+        assertTrue( AttributeUtils.containsValueCaseIgnore( teachers.get( "uniqueMember"
), testEntryName ) );
+    }
+ 
+}

Added: directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/sp/JavaStoredProcEngine.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/sp/JavaStoredProcEngine.java?view=auto&rev=555595
==============================================================================
--- directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/sp/JavaStoredProcEngine.java
(added)
+++ directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/sp/JavaStoredProcEngine.java
Thu Jul 12 04:46:08 2007
@@ -0,0 +1,129 @@
+/*
+ *  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.apache.directory.server.core.sp;
+
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.ldap.LdapContext;
+
+import org.apache.directory.shared.ldap.util.DirectoryClassUtils;
+
+
+public class JavaStoredProcEngine implements StoredProcEngine
+{
+
+    public static final String storedProcLangId = "Java";
+
+    private Attributes spUnit;
+
+
+    public Object invokeProcedure( LdapContext rootCtx, String fullSPName, Object[] spArgs
) throws NamingException
+    {
+        Attribute javaByteCode = spUnit.get( "javaByteCode" );
+        String spName = StoredProcUtils.extractStoredProcName( fullSPName );
+        String className = StoredProcUtils.extractStoredProcUnitName( fullSPName );
+
+        ClassLoader loader = new LdapJavaStoredProcClassLoader( javaByteCode );
+        Class clazz;
+        try
+        {
+            clazz = loader.loadClass( className );
+        }
+        catch ( ClassNotFoundException e )
+        {
+            NamingException ne = new NamingException();
+            ne.setRootCause( e );
+            throw ne;
+        }
+
+        Class[] types = getTypesFromValues( spArgs );
+
+        Method proc;
+        try
+        {
+            proc = DirectoryClassUtils.getAssignmentCompatibleMethod( clazz, spName, types
);
+        }
+        catch ( NoSuchMethodException e )
+        {
+            NamingException ne = new NamingException();
+            ne.setRootCause( e );
+            throw ne;
+        }
+        try
+        {
+            return proc.invoke( null, spArgs );
+        }
+        catch ( IllegalArgumentException e )
+        {
+            NamingException ne = new NamingException();
+            ne.setRootCause( e );
+            throw ne;
+        }
+        catch ( IllegalAccessException e )
+        {
+            NamingException ne = new NamingException();
+            ne.setRootCause( e );
+            throw ne;
+        }
+        catch ( InvocationTargetException e )
+        {
+            NamingException ne = new NamingException();
+            ne.setRootCause( e );
+            throw ne;
+        }
+    }
+
+
+    public String getSPLangId()
+    {
+        return storedProcLangId;
+    }
+
+
+    public void setSPUnitEntry( Attributes spUnit )
+    {
+        this.spUnit = spUnit;
+    }
+
+
+    private Class[] getTypesFromValues( Object[] values )
+    {
+        List<Class> types = new ArrayList<Class>();
+
+        for ( Object obj : values )
+        {
+            types.add( obj.getClass() );
+        }
+
+        return types.toArray( EMPTY_CLASS_ARRAY );
+    }
+
+    private static Class[] EMPTY_CLASS_ARRAY = new Class[ 0 ];
+
+}

Added: directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/sp/JavaStoredProcEngineConfig.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/sp/JavaStoredProcEngineConfig.java?view=auto&rev=555595
==============================================================================
--- directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/sp/JavaStoredProcEngineConfig.java
(added)
+++ directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/sp/JavaStoredProcEngineConfig.java
Thu Jul 12 04:46:08 2007
@@ -0,0 +1,38 @@
+/*
+ *  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.apache.directory.server.core.sp;
+
+public class JavaStoredProcEngineConfig implements StoredProcEngineConfig
+{
+
+    public Class<? extends StoredProcEngine> getStoredProcEngineType()
+    {
+        return JavaStoredProcEngine.class;
+    }
+
+
+    public String getStoredProcLangId()
+    {
+        return JavaStoredProcEngine.storedProcLangId;
+    }
+
+}

Added: directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/sp/LdapJavaStoredProcClassLoader.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/sp/LdapJavaStoredProcClassLoader.java?view=auto&rev=555595
==============================================================================
--- directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/sp/LdapJavaStoredProcClassLoader.java
(added)
+++ directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/sp/LdapJavaStoredProcClassLoader.java
Thu Jul 12 04:46:08 2007
@@ -0,0 +1,62 @@
+/*
+ *  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.apache.directory.server.core.sp;
+
+
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+
+
+/**
+ * A class loader that loads a class from an attribute.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$ $Date$
+ */
+public class LdapJavaStoredProcClassLoader extends ClassLoader
+{
+    private Attribute javaByteCodeAttr;
+
+
+    public LdapJavaStoredProcClassLoader( Attribute javaByteCodeAttr ) throws NamingException
+    {
+        // Critical call to super class constructor. Required for true plumbing of class
loaders.
+        super( LdapJavaStoredProcClassLoader.class.getClassLoader() );
+
+        this.javaByteCodeAttr = javaByteCodeAttr;
+    }
+
+
+    public Class<?> findClass( String name ) throws ClassNotFoundException
+    {
+        byte[] classBytes;
+        try
+        {
+            classBytes = ( byte[] ) javaByteCodeAttr.get();
+        }
+        catch ( NamingException e )
+        {
+            throw new ClassNotFoundException();
+        }
+
+        return defineClass( name, classBytes, 0, classBytes.length );
+    }
+}

Added: directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/sp/StoredProcEngine.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/sp/StoredProcEngine.java?view=auto&rev=555595
==============================================================================
--- directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/sp/StoredProcEngine.java
(added)
+++ directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/sp/StoredProcEngine.java
Thu Jul 12 04:46:08 2007
@@ -0,0 +1,36 @@
+/*
+ *  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.apache.directory.server.core.sp;
+
+import javax.naming.NamingException;
+import javax.naming.directory.Attributes;
+import javax.naming.ldap.LdapContext;
+
+public interface StoredProcEngine
+{
+    public String getSPLangId();
+    
+    public void setSPUnitEntry( final Attributes spUnit );
+    
+    public Object invokeProcedure( LdapContext rootCtx, String fullSPName, Object[] spArgs
) throws NamingException;
+
+}

Added: directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/sp/StoredProcEngineConfig.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/sp/StoredProcEngineConfig.java?view=auto&rev=555595
==============================================================================
--- directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/sp/StoredProcEngineConfig.java
(added)
+++ directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/sp/StoredProcEngineConfig.java
Thu Jul 12 04:46:08 2007
@@ -0,0 +1,30 @@
+/*
+ *  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.apache.directory.server.core.sp;
+
+public interface StoredProcEngineConfig
+{
+    public Class<? extends StoredProcEngine> getStoredProcEngineType();
+    
+    public String getStoredProcLangId();
+
+}

Added: directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/sp/StoredProcExecutionManager.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/sp/StoredProcExecutionManager.java?view=auto&rev=555595
==============================================================================
--- directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/sp/StoredProcExecutionManager.java
(added)
+++ directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/sp/StoredProcExecutionManager.java
Thu Jul 12 04:46:08 2007
@@ -0,0 +1,100 @@
+/*
+ *  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.apache.directory.server.core.sp;
+
+
+import java.util.List;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+
+import javax.naming.ldap.LdapContext;
+
+
+public class StoredProcExecutionManager
+{
+
+    private final String spContainer;
+
+    private final List<StoredProcEngineConfig> spEngineConfigs;
+
+
+    public StoredProcExecutionManager( final String spContainer, final List<StoredProcEngineConfig>
spEngineConfigs )
+    {
+        this.spContainer = spContainer;
+        this.spEngineConfigs = spEngineConfigs;
+    }
+    
+    public Attributes findStoredProcUnit( LdapContext rootCtx, String fullSPName ) throws
NamingException
+    {
+        SearchControls controls = new SearchControls();
+        controls.setReturningAttributes( new String[] { "*" } );
+        controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        String spUnitName = StoredProcUtils.extractStoredProcUnitName( fullSPName );
+        String filter = "(storedProcUnitName=" + spUnitName + ")";
+        NamingEnumeration<SearchResult> results = rootCtx.search( spContainer, filter,
controls );
+        Attributes spUnitEntry = results.nextElement().getAttributes();
+        return spUnitEntry;
+    }
+
+
+    public StoredProcEngine getStoredProcEngineInstance( Attributes spUnitEntry ) throws
NamingException
+    {
+
+        String spLangId = ( String ) spUnitEntry.get( "storedProcLangId" ).get();
+
+        for ( StoredProcEngineConfig engineConfig : spEngineConfigs )
+        {
+            if ( engineConfig.getStoredProcLangId().equalsIgnoreCase( spLangId ) )
+            {
+                Class<? extends StoredProcEngine> engineType = engineConfig.getStoredProcEngineType();
+                StoredProcEngine engine;
+                try
+                {
+                    engine = engineType.newInstance();
+                }
+                catch ( InstantiationException e )
+                {
+                    NamingException ne = new NamingException();
+                    ne.setRootCause( e );
+                    throw ne;
+                }
+                catch ( IllegalAccessException e )
+                {
+                    NamingException ne = new NamingException();
+                    ne.setRootCause( e );
+                    throw ne;
+                }
+                engine.setSPUnitEntry( spUnitEntry );
+                return engine;
+            }
+
+        }
+
+        throw new NamingException( "Stored Procedure Language, " + spLangId + " is not supported."
);
+
+    }
+
+}

Added: directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/sp/StoredProcUtils.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/sp/StoredProcUtils.java?view=auto&rev=555595
==============================================================================
--- directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/sp/StoredProcUtils.java
(added)
+++ directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/sp/StoredProcUtils.java
Thu Jul 12 04:46:08 2007
@@ -0,0 +1,42 @@
+/*
+ *  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.apache.directory.server.core.sp;
+
+public class StoredProcUtils
+{
+    
+    public static final String SPUnitDelimiter = ":";
+
+    public static String extractStoredProcName( String fullSPName )
+    {
+        int delimiter = fullSPName.lastIndexOf( SPUnitDelimiter );
+        String spName = fullSPName.substring( delimiter + SPUnitDelimiter.length() );
+        return spName;
+    }
+    
+    public static String extractStoredProcUnitName( String fullSPName )
+    {
+        int delimiter = fullSPName.lastIndexOf( SPUnitDelimiter );
+        String className = fullSPName.substring( 0, delimiter );
+        return className;
+    }
+}

Modified: directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/trigger/TriggerService.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/trigger/TriggerService.java?view=diff&rev=555595&r1=555594&r2=555595
==============================================================================
--- directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/trigger/TriggerService.java
(original)
+++ directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/trigger/TriggerService.java
Thu Jul 12 04:46:08 2007
@@ -21,7 +21,6 @@
 package org.apache.directory.server.core.trigger;
 
 
-import java.lang.reflect.Method;
 import java.text.ParseException;
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -50,7 +49,10 @@
 import org.apache.directory.server.core.invocation.InvocationStack;
 import org.apache.directory.server.core.jndi.ServerLdapContext;
 import org.apache.directory.server.core.partition.PartitionNexusProxy;
-import org.apache.directory.server.core.sp.LdapClassLoader;
+import org.apache.directory.server.core.sp.JavaStoredProcEngineConfig;
+import org.apache.directory.server.core.sp.StoredProcEngine;
+import org.apache.directory.server.core.sp.StoredProcEngineConfig;
+import org.apache.directory.server.core.sp.StoredProcExecutionManager;
 import org.apache.directory.server.core.subtree.SubentryService;
 import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
 import org.apache.directory.shared.ldap.constants.SchemaConstants;
@@ -63,7 +65,6 @@
 import org.apache.directory.shared.ldap.trigger.TriggerSpecification;
 import org.apache.directory.shared.ldap.trigger.TriggerSpecificationParser;
 import org.apache.directory.shared.ldap.trigger.TriggerSpecification.SPSpec;
-import org.apache.directory.shared.ldap.util.DirectoryClassUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -94,6 +95,8 @@
 
     /** a Trigger Execution Authorizer */
     private TriggerExecutionAuthorizer triggerExecutionAuthorizer = new SimpleTriggerExecutionAuthorizer();
+    
+    private StoredProcExecutionManager manager;
 
     /**
      * Adds prescriptiveTrigger TriggerSpecificaitons to a collection of
@@ -232,6 +235,15 @@
                 }
             );
         chain = dirServCfg.getInterceptorChain();
+        
+        //StoredProcEngineConfig javaxScriptSPEngineConfig = new JavaxStoredProcEngineConfig();
+        StoredProcEngineConfig javaSPEngineConfig = new JavaStoredProcEngineConfig();
+        List<StoredProcEngineConfig> spEngineConfigs = new ArrayList<StoredProcEngineConfig>();
+        //spEngineConfigs.add( javaxScriptSPEngineConfig );
+        spEngineConfigs.add( javaSPEngineConfig );
+        String spContainer = "ou=Stored Procedures,ou=system";
+        manager = new StoredProcExecutionManager( spContainer, spEngineConfigs );
+        
         this.enabled = true; // TODO: Get this from the configuration if needed.
     }
 
@@ -574,50 +586,26 @@
         {
         	List<Object> arguments = new ArrayList<Object>();
         	arguments.addAll( injector.getArgumentsToInject( spSpec.getParameters() ) );
-        	List<Class> typeList = new ArrayList<Class>();
-            typeList.addAll( getTypesFromValues( arguments ) );
-            Class[] types = getTypesFromValues( arguments ).toArray( EMPTY_CLASS_ARRAY );
             Object[] values = arguments.toArray();
-            Object returnValue = executeProcedure( callerRootCtx, spSpec.getName(), types,
values );
-            returnValues.add(returnValue);
+            Object returnValue = executeProcedure( callerRootCtx, spSpec.getName(), values
);
+            returnValues.add( returnValue );
 		}
         
         return returnValues; 
     }
+
     
-    private static Class[] EMPTY_CLASS_ARRAY = new Class[ 0 ];
-    
-    private List<Class> getTypesFromValues( List objects )
-    {
-        List<Class> types = new ArrayList<Class>();
-        
-        Iterator it = objects.iterator();
-        
-        while( it.hasNext() )
-        {
-            types.add( it.next().getClass() );
-        }
-        
-        return types;
-    }
-    
-    private Object executeProcedure( ServerLdapContext ctx, String procedure, Class[] types,
Object[] values ) throws NamingException
+    private Object executeProcedure( ServerLdapContext ctx, String procedure, Object[] values
) throws NamingException
     {
-        int lastDot = procedure.lastIndexOf( '.' );
-        String className = procedure.substring( 0, lastDot );
-        String methodName = procedure.substring( lastDot + 1 );
-        LdapClassLoader loader = new LdapClassLoader( ctx );
         
         try
         {
-            Class clazz = loader.loadClass( className );
-            Method proc = DirectoryClassUtils.getAssignmentCompatibleMethod( clazz, methodName,
types );
-            return proc.invoke( null, values );
+            Attributes spUnit = manager.findStoredProcUnit( ctx, procedure );
+            StoredProcEngine engine = manager.getStoredProcEngineInstance( spUnit );
+            return engine.invokeProcedure( ctx, procedure, values );
         }
-        catch ( Exception e )
+        catch ( NamingException e )
         {
-            log.debug( "Exception occured during executing stored procedure:\n" +
-                e.getMessage() + "\n" + e.getStackTrace() );
             LdapNamingException lne = new LdapNamingException( ResultCodeEnum.OTHER );
             lne.setRootCause( e );
             throw lne;

Added: directory/apacheds/trunk/core/src/test/java/org/apache/directory/server/core/sp/StoredProcUtilsTest.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/core/src/test/java/org/apache/directory/server/core/sp/StoredProcUtilsTest.java?view=auto&rev=555595
==============================================================================
--- directory/apacheds/trunk/core/src/test/java/org/apache/directory/server/core/sp/StoredProcUtilsTest.java
(added)
+++ directory/apacheds/trunk/core/src/test/java/org/apache/directory/server/core/sp/StoredProcUtilsTest.java
Thu Jul 12 04:46:08 2007
@@ -0,0 +1,42 @@
+/*
+ *  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.apache.directory.server.core.sp;
+
+import junit.framework.TestCase;
+
+public class StoredProcUtilsTest extends TestCase
+{
+
+    public void testSPNameTokenization()
+    {
+        String fullSPName = "Greeter:seyHello";
+        String expectedSPUnitName = "Greeter";
+        String expectedSPName = "seyHello";
+        
+        String actualSPUnitName = StoredProcUtils.extractStoredProcUnitName( fullSPName );
+        String actualSPName = StoredProcUtils.extractStoredProcName( fullSPName );
+        
+        assertEquals( expectedSPUnitName, actualSPUnitName );
+        assertEquals( expectedSPName, actualSPName );
+    }
+
+}

Added: directory/apacheds/trunk/server-unit/src/test/java/org/apache/directory/server/DITUtilitiesSP.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/server-unit/src/test/java/org/apache/directory/server/DITUtilitiesSP.java?view=auto&rev=555595
==============================================================================
--- directory/apacheds/trunk/server-unit/src/test/java/org/apache/directory/server/DITUtilitiesSP.java
(added)
+++ directory/apacheds/trunk/server-unit/src/test/java/org/apache/directory/server/DITUtilitiesSP.java
Thu Jul 12 04:46:08 2007
@@ -0,0 +1,64 @@
+/*
+ *  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.apache.directory.server;
+
+import javax.naming.Name;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import javax.naming.ldap.LdapContext;
+
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+public class DITUtilitiesSP
+{
+	private static final Logger log = LoggerFactory.getLogger( DITUtilitiesSP.class );
+	
+	/**
+     * Recursively deletes a subtree including the apex given.
+     * 
+     * If you do not want to wait for the developers to implement the
+     * following RFC
+     * http://kahuna.telstra.net/ietf/all-ids/draft-armijo-ldap-treedelete-02.txt
+     * you can do it yourself!
+     * 
+     * @param ctx an LDAP context to perform operations on
+     * @param rdn ctx relative name of the entry which is root of
+     *        the subtree to be deleted
+     * @throws NamingException
+     */
+    public static void deleteSubtree( LdapContext ctx, Name rdn ) throws NamingException
+    {
+        NamingEnumeration results = ctx.search( rdn, "(objectClass=*)", new SearchControls()
);
+        while ( results.hasMore() )
+        {
+            SearchResult result = ( SearchResult ) results.next();
+            Name childRdn = new LdapDN( result.getName() );
+            childRdn.remove( 0 );
+            deleteSubtree( ctx, childRdn );
+        }
+        ctx.destroySubcontext( rdn );
+        log.info( "Deleted: " + rdn );
+    }
+}

Added: directory/apacheds/trunk/server-unit/src/test/java/org/apache/directory/server/HelloWorldProcedure.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/server-unit/src/test/java/org/apache/directory/server/HelloWorldProcedure.java?view=auto&rev=555595
==============================================================================
--- directory/apacheds/trunk/server-unit/src/test/java/org/apache/directory/server/HelloWorldProcedure.java
(added)
+++ directory/apacheds/trunk/server-unit/src/test/java/org/apache/directory/server/HelloWorldProcedure.java
Thu Jul 12 04:46:08 2007
@@ -0,0 +1,34 @@
+/*
+ *  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.apache.directory.server;
+
+
+public class HelloWorldProcedure
+{
+    public static String sayHello()
+    {
+        return "Hello World!";
+    }
+    
+    public static String sayHelloTo( String name )
+    {
+        return "Hello " + name + "!";
+    }
+}



Mime
View raw message