-
Notifications
You must be signed in to change notification settings - Fork 746
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[GOBBLIN-236] Add a ControlMessage injector as a RecordStreamProcessor
- Loading branch information
Showing
17 changed files
with
592 additions
and
46 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
46 changes: 46 additions & 0 deletions
46
gobblin-api/src/main/java/org/apache/gobblin/metadata/GlobalMetadata.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
/* | ||
* 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.gobblin.metadata; | ||
|
||
import org.apache.gobblin.fork.CopyHelper; | ||
import org.apache.gobblin.fork.CopyNotSupportedException; | ||
import org.apache.gobblin.fork.Copyable; | ||
|
||
import lombok.AllArgsConstructor; | ||
import lombok.EqualsAndHashCode; | ||
import lombok.Getter; | ||
|
||
|
||
/** | ||
* Global metadata | ||
* @param <S> schema type | ||
*/ | ||
@AllArgsConstructor | ||
@EqualsAndHashCode | ||
public class GlobalMetadata<S> implements Copyable<GlobalMetadata<S>> { | ||
@Getter | ||
private S schema; | ||
|
||
@Override | ||
public GlobalMetadata<S> copy() throws CopyNotSupportedException { | ||
if (CopyHelper.isCopyable(schema)) { | ||
return new GlobalMetadata((S)CopyHelper.copy(schema)); | ||
} | ||
|
||
throw new CopyNotSupportedException("Type is not copyable: " + schema.getClass().getName()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
154 changes: 154 additions & 0 deletions
154
gobblin-api/src/main/java/org/apache/gobblin/stream/ControlMessageInjector.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
/* | ||
* 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.gobblin.stream; | ||
|
||
import java.io.Closeable; | ||
import java.io.IOException; | ||
|
||
import org.apache.gobblin.configuration.WorkUnitState; | ||
import org.apache.gobblin.metadata.GlobalMetadata; | ||
import org.apache.gobblin.records.ControlMessageHandler; | ||
import org.apache.gobblin.records.RecordStreamProcessor; | ||
import org.apache.gobblin.records.RecordStreamWithMetadata; | ||
|
||
import io.reactivex.Flowable; | ||
import lombok.AccessLevel; | ||
import lombok.Getter; | ||
import lombok.Setter; | ||
|
||
/** | ||
* A {@link RecordStreamProcessor} that inspects an input record and outputs control messages before, after, or around | ||
* the input record | ||
* @param <SI> | ||
* @param <DI> | ||
*/ | ||
public abstract class ControlMessageInjector<SI, DI> implements Closeable, | ||
RecordStreamProcessor<SI, SI, DI, DI> { | ||
|
||
@Setter(AccessLevel.PROTECTED) | ||
@Getter(AccessLevel.PROTECTED) | ||
private GlobalMetadata<SI> inputGlobalMetadata; | ||
|
||
/** | ||
* Initialize this {@link ControlMessageInjector}. | ||
* | ||
* @param workUnit a {@link WorkUnitState} object carrying configuration properties | ||
* @return an initialized {@link ControlMessageInjector} instance | ||
*/ | ||
public ControlMessageInjector<SI, DI> init(WorkUnitState workUnit) { | ||
return this; | ||
} | ||
|
||
@Override | ||
public void close() throws IOException { | ||
} | ||
|
||
/** | ||
* Set the global metadata of the input messages | ||
* @param inputGlobalMetadata the global metadata for input messages | ||
* @param workUnit | ||
*/ | ||
public void setInputGlobalMetadata(GlobalMetadata<SI> inputGlobalMetadata, WorkUnitState workUnit) { | ||
this.inputGlobalMetadata = inputGlobalMetadata; | ||
} | ||
|
||
/** | ||
* Inject {@link ControlMessage}s before the record | ||
* @param inputRecordEnvelope | ||
* @param workUnit | ||
* @return The {@link ControlMessage}s to inject before the record | ||
*/ | ||
public abstract Iterable<ControlMessage<DI>> injectControlMessagesBefore(RecordEnvelope<DI> inputRecordEnvelope, | ||
WorkUnitState workUnit); | ||
|
||
/** | ||
* Inject {@link ControlMessage}s after the record | ||
* @param inputRecordEnvelope | ||
* @param workUnit | ||
* @return The {@link ControlMessage}s to inject after the record | ||
*/ | ||
public abstract Iterable<ControlMessage<DI>> injectControlMessagesAfter(RecordEnvelope<DI> inputRecordEnvelope, | ||
WorkUnitState workUnit); | ||
|
||
/** | ||
* Apply injections to the input {@link RecordStreamWithMetadata}. | ||
* {@link ControlMessage}s may be injected before, after, or around the input record. | ||
* A {@link MetadataUpdateControlMessage} will update the current input {@link GlobalMetadata} and pass the | ||
* updated input {@link GlobalMetadata} to the next processor to propagate the metadata update down the pipeline. | ||
*/ | ||
@Override | ||
public RecordStreamWithMetadata<DI, SI> processStream(RecordStreamWithMetadata<DI, SI> inputStream, | ||
WorkUnitState workUnitState) throws StreamProcessingException { | ||
init(workUnitState); | ||
|
||
setInputGlobalMetadata(inputStream.getGlobalMetadata(), workUnitState); | ||
|
||
Flowable<StreamEntity<DI>> outputStream = | ||
inputStream.getRecordStream() | ||
.flatMap(in -> { | ||
if (in instanceof ControlMessage) { | ||
ControlMessage out = (ControlMessage) in; | ||
if (in instanceof MetadataUpdateControlMessage) { | ||
setInputGlobalMetadata(((MetadataUpdateControlMessage) in).getGlobalMetadata(), | ||
workUnitState); | ||
out = new MetadataUpdateControlMessage<SI>(this.inputGlobalMetadata); | ||
} | ||
|
||
getMessageHandler().handleMessage((ControlMessage) in); | ||
return Flowable.just(((ControlMessage<DI>) out)); | ||
|
||
} else if (in instanceof RecordEnvelope) { | ||
RecordEnvelope<DI> recordEnvelope = (RecordEnvelope<DI>) in; | ||
Iterable<ControlMessage<DI>> injectedBeforeIterable = | ||
injectControlMessagesBefore(recordEnvelope, workUnitState); | ||
Iterable<ControlMessage<DI>> injectedAfterIterable = | ||
injectControlMessagesAfter(recordEnvelope, workUnitState); | ||
|
||
if (injectedBeforeIterable == null && injectedAfterIterable == null) { | ||
// nothing injected so return the record envelope | ||
return Flowable.just(recordEnvelope); | ||
} else { | ||
Flowable<StreamEntity<DI>> flowable; | ||
|
||
if (injectedBeforeIterable != null) { | ||
flowable = Flowable.<StreamEntity<DI>>fromIterable(injectedBeforeIterable) | ||
.concatWith(Flowable.just(recordEnvelope)); | ||
} else { | ||
flowable = Flowable.just(recordEnvelope); | ||
} | ||
|
||
if (injectedAfterIterable != null) { | ||
flowable.concatWith(Flowable.fromIterable(injectedAfterIterable)); | ||
} | ||
return flowable; | ||
} | ||
} else { | ||
throw new UnsupportedOperationException(); | ||
} | ||
}, 1); | ||
outputStream = outputStream.doOnComplete(this::close); | ||
return inputStream.withRecordStream(outputStream, this.inputGlobalMetadata); | ||
} | ||
|
||
/** | ||
* @return {@link ControlMessageHandler} to call for each {@link ControlMessage} received. | ||
*/ | ||
public ControlMessageHandler getMessageHandler() { | ||
return ControlMessageHandler.NOOP; | ||
} | ||
} |
Oops, something went wrong.