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   *    https://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  import java.nio.charset.StandardCharsets;
31  
32  import javax.naming.NamingException;
33  import javax.naming.directory.Attributes;
34  import javax.naming.directory.BasicAttributes;
35  import javax.naming.ldap.ExtendedRequest;
36  import javax.naming.ldap.ExtendedResponse;
37  import javax.naming.ldap.LdapContext;
38  
39  import org.apache.commons.lang3.SerializationUtils;
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  import org.apache.directory.api.util.IOUtils;
44  
45  
46  /**
47   * A utility class for working with Java Stored Procedures at the base level.
48   *
49   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
50   */
51  public final class JavaStoredProcUtils
52  {
53  
54      /**
55       * Private constructor.
56       */
57      private JavaStoredProcUtils()
58      {
59      }
60  
61  
62      /**
63       * Returns the stream data of a Java class.
64       *
65       * @param clazz
66       *           The class whose stream data will be retrieved.
67       * @return
68       *           Stream data of the class file as a byte array.
69       * @throws NamingException
70       *           If an IO error occurs during reading the class file.
71       */
72      public static byte[] getClassFileAsStream( Class<?> clazz ) throws NamingException
73      {
74          String fullClassName = clazz.getName();
75          int lastDot = fullClassName.lastIndexOf( '.' );
76          String classFileName = fullClassName.substring( lastDot + 1 ) + ".class";
77          URL url = clazz.getResource( classFileName );
78          InputStream in = null;
79          
80          try
81          {
82              in = url.openStream();
83              File file = new File( url.toURI() );
84              int size = ( int ) file.length();
85              byte[] buf = new byte[size];
86              in.read( buf );
87  
88              return buf;
89          }
90          catch ( URISyntaxException urie )
91          {
92              NamingException ne = new NamingException();
93              ne.setRootCause( urie );
94              throw ne;
95          }
96          catch ( IOException ioe )
97          {
98              NamingException ne = new NamingException();
99              ne.setRootCause( ioe );
100             throw ne;
101         }
102         finally
103         {
104             if ( in != null )
105             {
106                 IOUtils.closeQuietly( in );
107             }
108         }
109     }
110 
111 
112     /**
113      * Loads a Java class's stream data as a subcontext of an LdapContext given.
114      *
115      * @param ctx
116      *           The parent context of the Java class entry to be loaded.
117      * @param clazz
118      *           Class to be loaded.
119      * @throws NamingException
120      *           If an error occurs during creating the subcontext.
121      */
122     public static void loadStoredProcedureClass( LdapContext ctx, Class<?> clazz ) throws NamingException
123     {
124         byte[] buf = getClassFileAsStream( clazz );
125         String fullClassName = clazz.getName();
126 
127         Attributes attributes = new BasicAttributes( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.TOP_OC, true );
128         attributes.get( SchemaConstants.OBJECT_CLASS_AT ).add( "storedProcUnit" );
129         attributes.get( SchemaConstants.OBJECT_CLASS_AT ).add( "javaStoredProcUnit" );
130         attributes.put( "storedProcLangId", "Java" );
131         attributes.put( "storedProcUnitName", fullClassName );
132         attributes.put( "javaByteCode", buf );
133 
134         ctx.createSubcontext( "storedProcUnitName=" + fullClassName, attributes );
135     }
136 
137 
138     /**
139      * Invoke a Stored Procedure
140      *
141      * @param ctx The execution context
142      * @param procedureName The procedure to execute
143      * @param arguments The procedure's arguments
144      * @return The execution resut
145      * @throws NamingException If we have had an error whil executing the stored procedure
146      */
147     public static Object callStoredProcedure( LdapContext ctx, String procedureName, Object[] arguments )
148         throws NamingException
149     {
150         String language = "Java";
151 
152         Object responseObject;
153         try
154         {
155             /**
156              * Create a new stored procedure execution request.
157              */
158             StoredProcedureRequestImpl req = new StoredProcedureRequestImpl( 0, procedureName, language );
159 
160             /**
161              * For each argument UTF-8-encode the type name
162              * and Java-serialize the value
163              * and add them to the request as a parameter object.
164              */
165             for ( int i = 0; i < arguments.length; i++ )
166             {
167                 byte[] type;
168                 byte[] value;
169                 type = arguments[i].getClass().getName().getBytes( StandardCharsets.UTF_8 );
170                 value = SerializationUtils.serialize( ( Serializable ) arguments[i] );
171                 req.addParameter( type, value );
172             }
173 
174             /**
175              * Call the stored procedure via the extended operation
176              * and get back its return value.
177              */
178             ExtendedRequest jndiReq = LdapApiServiceFactory.getSingleton().toJndi( req );
179             ExtendedResponse resp = ctx.extendedOperation( jndiReq );
180 
181             /**
182              * Restore a Java object from the return value.
183              */
184             byte[] responseStream = resp.getEncodedValue();
185             responseObject = SerializationUtils.deserialize( responseStream );
186         }
187         catch ( Exception e )
188         {
189             NamingException ne = new NamingException();
190             ne.setRootCause( e );
191             throw ne;
192         }
193 
194         return responseObject;
195     }
196 
197 }