View Javadoc
1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    * 
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   * 
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License.
18   * 
19   */
20  
21  package org.apache.directory.api.ldap.sp;
22  
23  
24  import java.io.File;
25  import java.io.IOException;
26  import java.io.InputStream;
27  import java.io.Serializable;
28  import java.net.URISyntaxException;
29  import java.net.URL;
30  
31  import javax.naming.NamingException;
32  import javax.naming.directory.Attributes;
33  import javax.naming.directory.BasicAttributes;
34  import javax.naming.ldap.ExtendedRequest;
35  import javax.naming.ldap.ExtendedResponse;
36  import javax.naming.ldap.LdapContext;
37  
38  import org.apache.commons.lang.SerializationUtils;
39  import org.apache.commons.io.IOUtils;
40  import org.apache.directory.api.ldap.codec.api.LdapApiServiceFactory;
41  import org.apache.directory.api.ldap.extras.extended.storedProcedure.StoredProcedureRequestImpl;
42  import org.apache.directory.api.ldap.model.constants.SchemaConstants;
43  
44  
45  /**
46   * A utility class for working with Java Stored Procedures at the base level.
47   *
48   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
49   */
50  public final class JavaStoredProcUtils
51  {
52  
53      /**
54       * Private constructor.
55       */
56      private JavaStoredProcUtils()
57      {
58      }
59  
60  
61      /**
62       * Returns the stream data of a Java class.
63       * 
64       * @param clazz
65       *           The class whose stream data will be retrieved.
66       * @return
67       *           Stream data of the class file as a byte array.
68       * @throws NamingException
69       *           If an IO error occurs during reading the class file.
70       */
71      public static byte[] getClassFileAsStream( Class<?> clazz ) throws NamingException
72      {
73          String fullClassName = clazz.getName();
74          int lastDot = fullClassName.lastIndexOf( '.' );
75          String classFileName = fullClassName.substring( lastDot + 1 ) + ".class";
76          URL url = clazz.getResource( classFileName );
77          InputStream in;
78          
79          try
80          {
81              in = url.openStream();
82          }
83          catch ( IOException ioe )
84          {
85              NamingException ne = new NamingException();
86              ne.setRootCause( ioe );
87              throw ne;
88          }
89          
90          File file;
91          
92          try
93          {
94              file = new File( url.toURI() );
95          }
96          catch ( URISyntaxException urie )
97          {
98              NamingException ne = new NamingException();
99              ne.setRootCause( urie );
100             throw ne;
101         }
102         
103         int size = ( int ) file.length();
104         byte[] buf = new byte[size];
105 
106         try
107         {
108             in.read( buf );
109         }
110         catch ( IOException e )
111         {
112             NamingException ne = new NamingException();
113             ne.setRootCause( e );
114             throw ne;
115         }
116         finally
117         {
118             IOUtils.closeQuietly( in );
119         }
120 
121         return buf;
122     }
123 
124 
125     /**
126      * Loads a Java class's stream data as a subcontext of an LdapContext given.
127      * 
128      * @param ctx
129      *           The parent context of the Java class entry to be loaded.
130      * @param clazz
131      *           Class to be loaded.
132      * @throws NamingException
133      *           If an error occurs during creating the subcontext.
134      */
135     public static void loadStoredProcedureClass( LdapContext ctx, Class<?> clazz ) throws NamingException
136     {
137         byte[] buf = getClassFileAsStream( clazz );
138         String fullClassName = clazz.getName();
139 
140         Attributes attributes = new BasicAttributes( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.TOP_OC, true );
141         attributes.get( SchemaConstants.OBJECT_CLASS_AT ).add( "storedProcUnit" );
142         attributes.get( SchemaConstants.OBJECT_CLASS_AT ).add( "javaStoredProcUnit" );
143         attributes.put( "storedProcLangId", "Java" );
144         attributes.put( "storedProcUnitName", fullClassName );
145         attributes.put( "javaByteCode", buf );
146 
147         ctx.createSubcontext( "storedProcUnitName=" + fullClassName, attributes );
148     }
149 
150 
151     public static Object callStoredProcedure( LdapContext ctx, String procedureName, Object[] arguments )
152         throws NamingException
153     {
154         String language = "Java";
155 
156         Object responseObject;
157         try
158         {
159             /**
160              * Create a new stored procedure execution request.
161              */
162             StoredProcedureRequestImpl req = new StoredProcedureRequestImpl( 0, procedureName, language );
163 
164             /**
165              * For each argument UTF-8-encode the type name
166              * and Java-serialize the value
167              * and add them to the request as a parameter object.
168              */
169             for ( int i = 0; i < arguments.length; i++ )
170             {
171                 byte[] type;
172                 byte[] value;
173                 type = arguments[i].getClass().getName().getBytes( "UTF-8" );
174                 value = SerializationUtils.serialize( ( Serializable ) arguments[i] );
175                 req.addParameter( type, value );
176             }
177 
178             /**
179              * Call the stored procedure via the extended operation
180              * and get back its return value.
181              */
182             ExtendedRequest jndiReq = LdapApiServiceFactory.getSingleton().toJndi( req );
183             ExtendedResponse resp = ctx.extendedOperation( jndiReq );
184 
185             /**
186              * Restore a Java object from the return value.
187              */
188             byte[] responseStream = resp.getEncodedValue();
189             responseObject = SerializationUtils.deserialize( responseStream );
190         }
191         catch ( Exception e )
192         {
193             NamingException ne = new NamingException();
194             ne.setRootCause( e );
195             throw ne;
196         }
197 
198         return responseObject;
199     }
200 
201 }