blob: 121315015697b962213f2ac83ce19b5e15c05b33 [file]
## Apache Spark Component
*Available as of Camel version 2.17*
This documentation page covers the http://spark.apache.org/[Apache
Spark] component for the Apache Camel. The main purpose of the Spark
integration with Camel is to provide a bridge between Camel connectors
and Spark tasks. In particular Camel connector provides a way to route
message from various transports, dynamically choose a task to execute,
use incoming message as input data for that task and finally deliver the
results of the execution back to the Camel pipeline.
### Supported architectural styles
Spark component can be used as a driver application deployed into an
application server (or executed as a fat jar).
image:apache-spark.data/camel_spark_driver.png[image] +
Spark component can also be submitted as a job directly into the Spark
cluster.
image:apache-spark.data/camel_spark_cluster.png[image] +
While Spark component is primary designed to work as a _long running
job_ serving as an bridge between Spark cluster and the other endpoints,
you can also use it as a _fire-once_ short job.   
### Running Spark in OSGi servers
Currently the Spark component doesn't support execution in the OSGi
container. Spark has been designed to be executed as a fat jar, usually
submitted as a job to a cluster. For those reasons running Spark in an
OSGi server is at least challenging and is not support by Camel as well.
### URI format
Currently the Spark component supports only producers - it it intended
to invoke a Spark job and return results. You can call RDD, data frame
or Hive SQL job.
*Spark URI format*
[source,java]
--------------------------
spark:{rdd|dataframe|hive}
--------------------------
#### Spark options
// component options: START
The Apache Spark component supports 3 options which are listed below.
[width="100%",cols="2,5,^1,2",options="header"]
|=======================================================================
| Name | Description | Default | Type
| **rdd** (producer) | RDD to compute against. | | JavaRDDLike
| **rddCallback** (producer) | Function performing action against an RDD. | | RddCallback
| **resolveProperty Placeholders** (advanced) | Whether the component should resolve property placeholders on itself when starting. Only properties which are of String type can use property placeholders. | true | boolean
|=======================================================================
// component options: END
// endpoint options: START
The Apache Spark endpoint is configured using URI syntax:
spark:endpointType
with the following path and query parameters:
#### Path Parameters (1 parameters):
[width="100%",cols="2,5,^1,2",options="header"]
|=======================================================================
| Name | Description | Default | Type
| **endpointType** | *Required* Type of the endpoint (rdd dataframe hive). | | EndpointType
|=======================================================================
#### Query Parameters (6 parameters):
[width="100%",cols="2,5,^1,2",options="header"]
|=======================================================================
| Name | Description | Default | Type
| **collect** (producer) | Indicates if results should be collected or counted. | true | boolean
| **dataFrame** (producer) | DataFrame to compute against. | | DataFrame
| **dataFrameCallback** (producer) | Function performing action against an DataFrame. | | DataFrameCallback
| **rdd** (producer) | RDD to compute against. | | JavaRDDLike
| **rddCallback** (producer) | Function performing action against an RDD. | | RddCallback
| **synchronous** (advanced) | Sets whether synchronous processing should be strictly used or Camel is allowed to use asynchronous processing (if supported). | false | boolean
|=======================================================================
// endpoint options: END
 
### RDD jobs 
To invoke an RDD job, use the following URI:
*Spark RDD producer*
[source,java]
------------------------------------------------------
spark:rdd?rdd=#testFileRdd&rddCallback=#transformation
------------------------------------------------------
 Where `rdd` option refers to the name of an RDD instance (subclass of
`org.apache.spark.api.java.JavaRDDLike`) from a Camel registry, while
`rddCallback` refers to the implementation
of `org.apache.camel.component.spark.RddCallback` interface (also from a
registry). RDD callback provides a single method used to apply incoming
messages against the given RDD. Results of callback computations are
saved as a body to an exchange.
*Spark RDD callback*
[source,java]
-------------------------------------------------
public interface RddCallback<T> {
T onRdd(JavaRDDLike rdd, Object... payloads);
}
-------------------------------------------------
The following snippet demonstrates how to send message as an input to
the job and return results:
*Calling spark job*
[source,java]
------------------------------------------------------------------------------------------------------------------------------
String pattern = "job input";
long linesCount = producerTemplate.requestBody("spark:rdd?rdd=#myRdd&rddCallback=#countLinesContaining", pattern, long.class);
------------------------------------------------------------------------------------------------------------------------------
The RDD callback for the snippet above registered as Spring bean could
look as follows:
*Spark RDD callback*
[source,java]
------------------------------------------------------------------------
@Bean
RddCallback<Long> countLinesContaining() {
return new RddCallback<Long>() {
Long onRdd(JavaRDDLike rdd, Object... payloads) {
String pattern = (String) payloads[0];
return rdd.filter({line -> line.contains(pattern)}).count();
}
}
}
------------------------------------------------------------------------
The RDD definition in Spring could looks as follows:
*Spark RDD definition*
[source,java]
--------------------------------------------------
@Bean
JavaRDDLike myRdd(JavaSparkContext sparkContext) {
return sparkContext.textFile("testrdd.txt");
}
--------------------------------------------------
#### Void RDD callbacks
If your RDD callback doesn't return any value back to a Camel pipeline,
you can either return `null` value or use `VoidRddCallback` base class:
*Spark RDD definition*
[source,java]
------------------------------------------------------------------
@Bean
RddCallback<Void> rddCallback() {
return new VoidRddCallback() {
@Override
public void doOnRdd(JavaRDDLike rdd, Object... payloads) {
rdd.saveAsTextFile(output.getAbsolutePath());
}
};
}
------------------------------------------------------------------
#### Converting RDD callbacks
If you know what type of the input data will be sent to the RDD
callback, you can use `ConvertingRddCallback` and let Camel to
automatically convert incoming messages before inserting those into the
callback:
*Spark RDD definition*
[source,java]
---------------------------------------------------------------------------
@Bean
RddCallback<Long> rddCallback(CamelContext context) {
return new ConvertingRddCallback<Long>(context, int.class, int.class) {
@Override
public Long doOnRdd(JavaRDDLike rdd, Object... payloads) {
return rdd.count() * (int) payloads[0] * (int) payloads[1];
}
};
};
}
---------------------------------------------------------------------------
#### Annotated RDD callbacks
Probably the easiest way to work with the RDD callbacks is to provide
class with method marked with `@RddCallback` annotation:
*Annotated RDD callback definition*
[source,java]
-----------------------------------------------------------------------------------------------------
import static org.apache.camel.component.spark.annotations.AnnotatedRddCallback.annotatedRddCallback;
 
@Bean
RddCallback<Long> rddCallback() {
return annotatedRddCallback(new MyTransformation());
}
 
...
 
import org.apache.camel.component.spark.annotation.RddCallback;
 
public class MyTransformation {
 
@RddCallback
long countLines(JavaRDD<String> textFile, int first, int second) {
return textFile.count() * first * second;
}
 
}
-----------------------------------------------------------------------------------------------------
If you will pass CamelContext to the annotated RDD callback factory
method, the created callback will be able to convert incoming payloads
to match the parameters of the annotated method:
*Body conversions for annotated RDD callbacks*
[source,java]
------------------------------------------------------------------------------------------------------------------------------
import static org.apache.camel.component.spark.annotations.AnnotatedRddCallback.annotatedRddCallback;
 
@Bean
RddCallback<Long> rddCallback(CamelContext camelContext) {
return annotatedRddCallback(new MyTransformation(), camelContext);
}
 
...
 
import org.apache.camel.component.spark.annotation.RddCallback;
 
public class MyTransformation {
 
@RddCallback
long countLines(JavaRDD<String> textFile, int first, int second) {
return textFile.count() * first * second;
}
 
}
 
...
 
// Convert String "10" to integer
long result = producerTemplate.requestBody("spark:rdd?rdd=#rdd&rddCallback=#rddCallback" Arrays.asList(10, "10"), long.class);
------------------------------------------------------------------------------------------------------------------------------
 
### DataFrame jobs
Instead of working with RDDs Spark component can work with DataFrames as
well. 
To invoke an DataFrame job, use the following URI:
*Spark RDD producer*
[source,java]
--------------------------------------------------------------------------
spark:dataframe?dataFrame=#testDataFrame&dataFrameCallback=#transformation
--------------------------------------------------------------------------
 Where `dataFrame` option refers to the name of an DataFrame instance
(`instance of of org.apache.spark.sql.DataFrame`) from a Camel registry,
while `dataFrameCallback` refers to the implementation
of `org.apache.camel.component.spark.DataFrameCallback` interface (also
from a registry). DataFrame callback provides a single method used to
apply incoming messages against the given DataFrame. Results of callback
computations are saved as a body to an exchange.
*Spark RDD callback*
[source,java]
-----------------------------------------------------------
public interface DataFrameCallback<T> {
T onDataFrame(DataFrame dataFrame, Object... payloads);
}
-----------------------------------------------------------
The following snippet demonstrates how to send message as an input to a
job and return results:
*Calling spark job*
[source,java]
-----------------------------------------------------------------------------------------------------------------------------------------
String model = "Micra";
long linesCount = producerTemplate.requestBody("spark:dataFrame?dataFrame=#cars&dataFrameCallback=#findCarWithModel", model, long.class);
-----------------------------------------------------------------------------------------------------------------------------------------
The DataFrame callback for the snippet above registered as Spring bean
could look as follows:
*Spark RDD callback*
[source,java]
-------------------------------------------------------------------------------------
@Bean
RddCallback<Long> findCarWithModel() {
return new DataFrameCallback<Long>() {
@Override
public Long onDataFrame(DataFrame dataFrame, Object... payloads) {
String model = (String) payloads[0];
return dataFrame.where(dataFrame.col("model").eqNullSafe(model)).count();
}
};
}
-------------------------------------------------------------------------------------
The DataFrame definition in Spring could looks as follows:
*Spark RDD definition*
[source,java]
------------------------------------------------------------------------
@Bean
DataFrame cars(HiveContext hiveContext) {
DataFrame jsonCars = hiveContext.read().json("/var/data/cars.json");
jsonCars.registerTempTable("cars");
return jsonCars;
}
------------------------------------------------------------------------
### Hive jobs
 Instead of working with RDDs or DataFrame Spark component can also
receive Hive SQL queries as payloads. To send Hive query to Spark
component, use the following URI:
*Spark RDD producer*
[source,java]
----------
spark:hive
----------
The following snippet demonstrates how to send message as an input to a
job and return results:
*Calling spark job*
[source,java]
----------------------------------------------------------------------------------------------------
long carsCount = template.requestBody("spark:hive?collect=false", "SELECT * FROM cars", Long.class);
List<Row> cars = template.requestBody("spark:hive", "SELECT * FROM cars", List.class);
----------------------------------------------------------------------------------------------------
The table we want to execute query against should be registered in a
HiveContext before we query it. For example in Spring such registration
could look as follows:
*Spark RDD definition*
[source,java]
------------------------------------------------------------------------
@Bean
DataFrame cars(HiveContext hiveContext) {
DataFrame jsonCars = hiveContext.read().json("/var/data/cars.json");
jsonCars.registerTempTable("cars");
return jsonCars;
}
------------------------------------------------------------------------
### See Also
* link:configuring-camel.html[Configuring Camel]
* link:component.html[Component]
* link:endpoint.html[Endpoint]
* link:getting-started.html[Getting Started]