I am working on a web_application sing Java,servlets,JSP and using apache Tomcat as application server
What I have done
i have created a UI where user is selecting mail Ids (they can select more than one)
And when user is clicking on send button i am triggering my java class and sending the mail
Now What i have to do
Now i have to do this dynamically,every night at 12:00 O'clock i have to send mail to some particular users
User to whom i have to send mail i am getting that mail id from login query so that is not an issue
I just want to know how can I send mail when it is midnight 12:00 O'clock
Codding I have done till now
servlet class
public class EmailSendingServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private String host;
private String port;
private String user;
private String pass;
public void init() {
ServletContext context = getServletContext();
host = context.getInitParameter("host");
port = context.getInitParameter("port");
user = context.getInitParameter("user");
pass = context.getInitParameter("pass");
}
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
String recipient = request.getParameter("To"); // this i will get from login query
String subject = request.getParameter("subject");//this i can define manually
String content = request.getParameter("content");//same for this also
String resultMessage = "";
try {
EmailUtility.sendEmail(host, port, user, pass, recipient, subject,
content);
resultMessage = "The e-mail was sent successfully";
} catch (Exception ex) {
ex.printStackTrace();
resultMessage = "There were an error: " + ex.getMessage();
}
}
}
Java Utility classs
public class EmailUtility {
public static void sendEmail(String host, String port, final String userName, final String password,
String toAddress, String subject, String message) throws AddressException, MessagingException {
Properties properties = new Properties();
properties.put("mail.smtp.host", host);
properties.put("mail.smtp.port", port);
properties.put("mail.smtp.auth", "true");
properties.put("mail.smtp.starttls.enable", "true");
Session session = Session.getDefaultInstance(properties, new javax.mail.Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(userName, password);
}
});
session.setDebug(false);
Message msg = new MimeMessage(session);
msg.setFrom(new InternetAddress(userName));
if (toAddress!= null) {
List<String> emails = new ArrayList<>();
if (toAddress.contains(",")) {
emails.addAll(Arrays.asList(toAddress.split(",")));
} else {
emails.add(toAddress);
}
Address[] to = new Address[emails.size()];
int counter = 0;
for(String email : emails) {
to[counter] = new InternetAddress(email.trim());
counter++;
}
msg.setRecipients(Message.RecipientType.TO, to);
}
msg.setSubject(subject);
msg.setSentDate(new Date());
msg.setText(message);
Transport.send(msg);
}
}
You can use a ScheduledExecutorService for handling this in "plain" Java:
ScheduledExecutorService ses = Executors.newScheduledThreadPool(1);
int count = 0;
Runnable task = () -> {
count++;
System.out.println(count);
};
ScheduledFuture<?> scheduledFuture = ses.scheduleAtFixedRate(task, 12, TimeUnit.HOURS);
There is also a method for using an initial delay, but you can read more here:
https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ScheduledExecutorService.html
For your use case:
Introduce a new class EmailSendJobUtil:
public class EmailSendUtil {
public void createAndSubmitSheduledJob() {
ScheduledExecutorService ses = Executors.sheduledThreadPool(1);
ScheduledFuture<> sheduledFuture = ses.sheduleAtFixedRate(EmailUtility.sendMail(), 12, TimeUnit.HOURS);
}
}
However, you will get troubles with your code structure. Try to introduce a method in your EmailUtility that encapsulates the automatated sending of mails.
Introduce a repository for saving to which users you have to send mails automatically and read this data in the new method that only handles the automatic sending. You could do something like this:
public class MailJobRepository {
private List<MailJob> jobs;
void add();
void remove();
List<> getJobs();
}
And in your EmailUtility introduce a new method:
public void sendAutomatedEmails() {
jobRepository.getJobs().foreach(job -> {
sendMail(job.getToAddress(), job.getSubject(), job.getMessage());
});
}
Then you can shedule this new method and you have splitted your code into logical seperate parts.
Just a little hint:
String host, String port, final String userName, final String password
This is data for your "side" of the email sending and should not be passed as a method parameter. You can save this data into your EmailUtility class.
In java the scheduler is used to schedule a thread or task that executes at a certain period of time or periodically at a fixed interval. There are multiple ways to schedule a task in Java :
java.util.TimerTask
java.util.concurrent.ScheduledExecutorService
Quartz Scheduler
org.springframework.scheduling.TaskScheduler
For pure java implementation without any framework usage, using ScheduledExecutorService running a task at certain periods:
public void givenUsingExecutorService_whenSchedulingRepeatedTask_thenCorrect()
throws InterruptedException {
TimerTask repeatedTask = new TimerTask() {
public void run() {
EmailUtility.sendEmail(host, port, user, pass, recipient,object,content);
System.out.println("The e-mail was sent successfully");
}
};
ZonedDateTime now = ZonedDateTime.now(ZoneId.of("America/Los_Angeles"));
ZonedDateTime nextRun = now.withHour(5).withMinute(0).withSecond(0);
if(now.compareTo(nextRun) > 0)
nextRun = nextRun.plusDays(1);
Duration duration = Duration.between(now, nextRun);
long initalDelay = duration.getSeconds();
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
scheduler.scheduleAtFixedRate(repeatedTask,
initalDelay,
TimeUnit.DAYS.toSeconds(1),
TimeUnit.SECONDS);
executor.shutdown();
}
You should take a look to isocline's Clockwork it's a java process engine. It is capable of coding various functions more efficiently than Quartz, and has specific execution function.
Related
I'm trying to build a system in which I can connect some devices to a server over the internet.
I want to stream some data over CoAP (10-30FPS), frame size = 3KB.
Firstly, I used Aiocoap, it sends up to 100FPS but uses too much CPU,
requests are NON, got low lose rate in Aiocoap,
while Eclipse/Californium could not send more than 3FPS,
when i use higher FPS, either I receive only the first block of each message or receiving nothing, also not ordered most of the times.
I was wondering if this is the real performance of Californium or am I using it in a wrong way?
I will share some code:
server.java
static class CoapObserverServer extends CoapResource {
int i = -1;
public CoapObserverServer() {
super("alarm");
setObservable(true); // enable observing
setObserveType(Type.NON); // configure the notification type to CONs
getAttributes().setObservable(); // mark observable in the Link-Format
System.out.println(this);
// schedule a periodic update task, otherwise let events call changed()
//new Timer().schedule(new UpdateTask(), 0, 1000/2);
}
private class UpdateTask extends TimerTask {
#Override
public void run() {
changed(); // notify all observers
}
}
#Override
public void handleGET(CoapExchange exchange) {
// the Max-Age value should match the update interval
exchange.setMaxAge(1);
//++i;
int leng = 2000;
String s = "" + i + "-" + fillString('X', leng - 1 - Integer.toString(i).len>
exchange.respond(s);
}
public static String fillString(char fillChar, int count){
// creates a string of 'x' repeating characters
char[] chars = new char[count];
while (count>0) chars[--count] = fillChar;
return new String(chars);
}
#Override
public void handleDELETE(CoapExchange exchange) {
delete(); // will also call clearAndNotifyObserveRelations(ResponseCode.NOT_>
exchange.respond(ResponseCode.DELETED);
}
#Override
public void handlePUT(CoapExchange exchange) {
exchange.accept();
int format = exchange.getRequestOptions().getContentFormat();
if (format == MediaTypeRegistry.TEXT_PLAIN) {
// ...
String plain = exchange.getRequestText();
try{
i = Integer.valueOf(plain);
} catch(NumberFormatException ex){
System.out.println("error converting string"+ plain);
}
exchange.respond(ResponseCode.CHANGED);
changed(); // notify all observers
}
}
Observer.java
private static final File CONFIG_FILE = new File("Californium3.properties");
private static final String CONFIG_HEADER = "Californium CoAP Properties file for client";
private static final int DEFAULT_MAX_RESOURCE_SIZE = 2 * 1024 * 1024; // 2 MB
private static final int DEFAULT_BLOCK_SIZE = 512;
static {
CoapConfig.register();
UdpConfig.register();
}
private static DefinitionsProvider DEFAULTS = new DefinitionsProvider() {
#Override
public void applyDefinitions(Configuration config) {
config.set(CoapConfig.MAX_RESOURCE_BODY_SIZE, DEFAULT_MAX_RESOURCE_SIZE);
config.set(CoapConfig.MAX_MESSAGE_SIZE, DEFAULT_BLOCK_SIZE);
config.set(CoapConfig.PREFERRED_BLOCK_SIZE, DEFAULT_BLOCK_SIZE);
}
};
private static class AsynchListener implements CoapHandler {
#Override
public void onLoad(CoapResponse response) {
System.out.println( response.getResponseText() );
}
#Override
public void onError() {
System.err.println("Error");
}
}
/*
* Application entry point.
*/
public static void main(String args[]) {
Configuration config = Configuration.createWithFile(CONFIG_FILE, CONFIG_HEADER, DEFAULTS);
Configuration.setStandard(config);
URI uri = null; // URI parameter of the request
if (args.length > 0) {
// input URI from command line arguments
try {
uri = new URI(args[0]);
} catch (URISyntaxException e) {
System.err.println("Invalid URI: " + e.getMessage());
System.exit(-1);
}
CoapClient client = new CoapClient(uri);
client.useNONs();
// observe
AsynchListener asynchListener = new AsynchListener();
CoapObserveRelation observation = client.observe(asynchListener);
// User presses ENTER to exit
System.out.println("Press ENTER to exit...");
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
try { br.readLine(); } catch (IOException e) { }
System.out.println("Exiting...");
observation.proactiveCancel();
}
So i'm controlling the FPS by sending PUT requests with a server that has a counter 0-50.
Not sure, what your doing.
That seems to be wired and not related to RFC7252 nor RFC7641.
CoAP is designed for REST, I don't see any benefit in using it for video streaming.
Using Eclipse/Californium on a Intel n6005 with 16GB RAM, the CoAP/DTLS server runs on about 60000 requests/second. The benchmark uses 2000 clients in parallel.
See also Eclipse/Californium - Benchmarks j5005
Using only one client with CON requests, the performance is mainly limited by the RTT. 30 requests/second should work, if that RTT is accordingly small.
Using NON requests doesn't really help. CoAP RFC7252 defines two layers, a messaging layer and an application layer. NON affects only the messaging layer, but a NON request will wait for it's response, if NSTART-1 should be used.
If your RTT is the issue, you may try to escape that either using requests with "No Server Response" (RFC7967) or multiple NON responses (RFC7641). The first is not intended for fast requests, the second is more a work-around of the initial statement, that CoAP is REST not video-streaming.
So, what is your RTT?
I have implemented a service of getting a file from, putting a file to, and removing a file from the SFTP server based on the SftpRemoteFileTemplate within Spring's Integration Package.
Here sftpGetPayload gets a file from the SFTP server and delivers its content.
This is my code so far:
public String sftpGetPayload(String sessionId,
String host, int port, String user, String password,
String remoteDir, String remoteFilename, boolean remoteRemove) {
LOG.info("sftpGetPayload sessionId={}", sessionId);
LOG.debug("sftpGetPayLoad host={}, port={}, user={}", host, port, user);
LOG.debug("sftpGetPayload remoteDir={}, remoteFilename={}, remoteRemove={}",
remoteDir, remoteFilename, remoteRemove);
final AtomicReference<String> refPayload = new AtomicReference<>();
SftpRemoteFileTemplate template = getSftpRemoteFileTemplate(host, port,
user, password, remoteDir, remoteFilename);
template.get(remoteDir + "/" + remoteFilename,
is -> refPayload.set(getAsString(is)));
LOG.info("sftpGetToFile {} read.", remoteDir + "/" + remoteFilename);
deleteRemoteFile(template, remoteDir, remoteFilename, remoteRemove);
return refPayload.get();
}
private SftpRemoteFileTemplate getSftpRemoteFileTemplate(String host, int port,
String user, String password, String remoteDir, String remoteFilename) {
SftpRemoteFileTemplate template =
new SftpRemoteFileTemplate(sftpSessionFactory(host, port, user, password));
template.setFileNameExpression(
new LiteralExpression(remoteDir + "/" + remoteFilename));
template.setRemoteDirectoryExpression(new LiteralExpression(remoteDir));
//template.afterPropertiesSet();
return template;
}
private void deleteRemoteFile(SftpRemoteFileTemplate template,
String remoteDir, String remoteFilename, boolean remoteRemove) {
LOG.debug("deleteRemoteFile remoteRemove={}", remoteRemove);
if (remoteRemove) {
template.remove(remoteDir + "/" + remoteFilename);
LOG.info("sftpGetToFile {} removed.", remoteDir + "/" + remoteFilename);
}
}
All those GET actions are active actions, meaning the file to get is considered to be already there. I would like to have a kind of a polling process, which calls my payload consuming method as soon as a file is received on the SFTP server.
I have found another implementation based on Spring beans, configured as Spring Integration Dsl, which declares a SftpSessionFactory, a SftpInboundFileSynchronizer, a SftpMessageSource, and a MessageHandler which polls a SFTP site for reception of a file and initiates a message handler automatically for further processing.
This code is as follows:
#Bean
public SessionFactory<ChannelSftp.LsEntry> sftpSessionFactory() {
DefaultSftpSessionFactory factory = new DefaultSftpSessionFactory(true);
factory.setHost(myHost);
factory.setPort(myPort);
factory.setUser(myUser);
factory.setPassword(myPassword);
factory.setAllowUnknownKeys(true);
return new CachingSessionFactory<LsEntry>(factory);
}
#Bean
public SftpInboundFileSynchronizer sftpInboundFileSynchronizer() {
SftpInboundFileSynchronizer fileSynchronizer = new SftpInboundFileSynchronizer(sftpSessionFactory());
fileSynchronizer.setDeleteRemoteFiles(false);
fileSynchronizer.setRemoteDirectory(myRemotePath);
fileSynchronizer.setFilter(new SftpSimplePatternFileListFilter(myFileFilter));
return fileSynchronizer;
}
#Bean
#InboundChannelAdapter(channel = "sftpChannel", poller = #Poller(fixedDelay = "5000"))
public MessageSource<File> sftpMessageSource() {
SftpInboundFileSynchronizingMessageSource source = new SftpInboundFileSynchronizingMessageSource(
sftpInboundFileSynchronizer());
source.setLocalDirectory(myLocalDirectory);
source.setAutoCreateLocalDirectory(true);
source.setLocalFilter(new AcceptOnceFileListFilter<File>());
return source;
}
#Bean
#ServiceActivator(inputChannel = "sftpChannel")
public MessageHandler handler() {
return new MessageHandler() {
#Override
public void handleMessage(Message<?> message) throws MessagingException {
System.out.println(message.getPayload());
}
};
}
How can I include this #Poller/MessageHandler/#ServiceActivator concept into my implementation above? Or is there a way to implement this feature in the template-based implementation?
The scenario could be following:
I have a Spring Boot Application with several classes which represent tasks. Some of those tasks are called automatically via the Spring #Scheduled annotation and a CRON specification, others are not.
#Scheduled(cron = "${task.to.start.automatically.frequency}")
public void runAsTask() {
...
}
First task will start at ist #Sheduled specification and get a file from SFTP server and process it. It will do that with its own channel (host1, port1, user1, password1, remoteDir1, remoteFile1).
Second task will also be run by the scheduler and generate something to put to the SFTP server. It will do that with its own channel (host2, port2, user2, password2, remoteDir2, remoteFile2). Very likely will host2 = host1 and port2 = port1, but it is not a must.
Third task will aslo be run by the scheduler and generate something to put to the SFTP server. It will do that with the same channel as task1, but this task is a producer (not a consumer like task1) and will write another file than task1 (host1, port1, user1, password1, remoteDir3, remoteFile3).
Task four has no #Scheduled annotation because it should realize when the file, it has to process, is received from third party and hence available on its channel (host4, port4, user4, password4, remoteDir4, remoteFile4) to get its content to process it.
I have read the whole Integration stuff, but it is hard to transform for this use case, either from the XML configuration schemes to Java configuration with annotations and also by the reather static Spring bean approach to a merly dynamic approach at runtime.
I understood to use an IntegrationFlow to register the artefacts, an inbound adapter for task1, an outbound adapter for task2, an inbound adapter for task3 with the same (anywhere else registrated) session factory of task1, and - last but not least - an inbound adapter with poller feature for task4.
Or should all of them be gateways with its command feature? Or should I register SftpRemoteFileTemplate?
To define the channel I have:
public class TransferChannel {
private String host;
private int port;
private String user;
private String password;
/* getters, setters, hash, equals, and toString */
}
To have all SFTP settings together, I have:
public class TransferContext {
private boolean enabled;
private TransferChannel channel;
private String remoteDir;
private String remoteFilename;
private boolean remoteRemove;
private String remoteFilenameFilter;
private String localDir;
/* getters, setters, hash, equals, and toString */
}
As the heart of the SFTP processing each job will inject kind of a DynamicSftpAdapter:
#Scheduled(cron = "${task.to.start.automatically.frequency}")
public void runAsTask() {
#Autowired
DynamicSftpAdapter sftp;
...
sftp.connect("Task1", context);
File f = sftp.getFile("Task1", "remoteDir", "remoteFile");
/* process file content */
sftp.removeFile("Task1", "remoteDir", "remoteFile");
sftp.disconnect("Task1", context);
}
The DynamicSftpAdapter is not much more than a fragment yet:
#Component
public class DynamicSftpAdapter {
private static final Logger LOG = LoggerFactory.getLogger(DynamicTcpServer.class);
#Autowired
private IntegrationFlowContext flowContext;
#Autowired
private ApplicationContext appContext;
private final Map<TransferChannel, IntegrationFlowRegistration> registrations = new HashMap<>();
private final Map<String, TransferContext> sessions = new ConcurrentHashMap<>();
#Override
public void connect(String sessionId, TransferContext context) {
if (this.registrations.containsKey(context.getChannel())) {
LOG.debug("connect, channel exists for {}", sessionId);
}
else {
// register the required SFTP Outbound Adapter
TransferChannel channel = context.getChannel();
IntegrationFlow flow = f -> f.handle(Sftp.outboundAdapter(cashedSftpSessionFactory(
channel.getHost(), channel.getPort(),
channel.getUser(), channel.getPassword())));
this.registrations.put(channel, flowContext.registration(flow).register());
this.sessions.put(sessionId, context);
LOG.info("sftp session {} for {} started", sessionId, context);
}
}
private DefaultSftpSessionFactory sftpSessionFactory(String host, int port, String user, String password) {
LOG.debug("sftpSessionFactory");
DefaultSftpSessionFactory factory = new DefaultSftpSessionFactory(true);
factory.setHost(host);
factory.setPort(port);
factory.setUser(user);
factory.setPassword(password);
factory.setAllowUnknownKeys(true);
return factory;
}
private CachingSessionFactory<LsEntry> cashedSftpSessionFactory(String host, int port, String user, String password) {
LOG.debug("cashedSftpSessionFactory");
CachingSessionFactory<LsEntry> cashedSessionFactory =
new CachingSessionFactory<LsEntry>(
sftpSessionFactory(host, port, user, password));
return cashedSessionFactory;
}
#Override
public void sftpGetFile(String sessionId, String remoteDir, String remoteFilename) {
TransferContext context = sessions.get(sessionId);
if (context == null)
throw new IllegalStateException("Session not established, sessionId " + sessionId);
IntegrationFlowRegistration register = registrations.get(context.getChannel());
if (register != null) {
try {
LOG.debug("sftpGetFile get file {}", remoteDir + "/" + remoteFilename);
register.getMessagingTemplate().send(
MessageBuilder.withPayload(msg)
.setHeader(...).build());
}
catch (Exception e) {
appContext.getBean(context, DefaultSftpSessionFactory.class)
.close();
}
}
}
#Override
public void disconnect(String sessionId, TransferContext context) {
IntegrationFlowRegistration registration = this.registrations.remove(context.getChannel());
if (registration != null) {
registration.destroy();
}
LOG.info("sftp session for {} finished", context);
}
}
I did not get how to initiate a SFTP command. I also did not get when using an OutboundGateway and having to specify the SFTP command (like GET) instantly, then would the whole SFTP handling be in one method, specifying the outbound gateway factory and getting an instance with get() and probably calling the message .get() in any way.
Obviously I need help.
First of all if you already use Spring Integration channel adapters, there is probably no reason to use that low-level API like RemoteFileTemplate directly.
Secondly there is a technical discrepancy: the SftpInboundFileSynchronizingMessageSource will produce a local file - a whole copy of the remote file. So, when we would come to your SftpRemoteFileTemplate logic downstream it would not work well since we would bring already just a local file (java.io.File), not an entity for remote file representation.
Even if your logic in the sftpGetPayload() doesn't look as complicated and custom as it would require such a separate method, it is better to have an SftpRemoteFileTemplate as a singleton and share it between different components when you work against the same SFTP server. It is just stateless straightforward Spring template pattern implementation.
If you still insist to use your method from the mentioned integration flow, you should consider to have a POJO method call for that #ServiceActivator(inputChannel = "sftpChannel"). See more in docs: https://docs.spring.io/spring-integration/docs/current/reference/html/configuration.html#annotations.
You also may find an SFTP Outbound Gateway as useful component for your use-case. It has some common scenarios implementations: https://docs.spring.io/spring-integration/docs/current/reference/html/sftp.html#sftp-outbound-gateway
I'm having some issues with Spring LocaleContextHolder.
I have the following code:
public void sendPasswordRecoverySmsAsync(String phone) {
CompletableFuture.runAsync(() -> {
sendPasswordRecoverySmsSync(phone);
});
}
public void sendPasswordRecoverySmsSync(String phone) {
User user = userDao.findByPhone(phone, User.class).orElseThrow(() -> new UserNotFoundException(phone));
log.info("User found, recovering password");
user.setUpdateTime(LocalDateTime.now());
userDao.save(user);
int otp = codesGenerator.generateOtp(user.getUpdateTime());
// Sends the SMS.
Locale locale = LocaleContextHolder.getLocale();
System.out.println("locale " + locale);
String appName = messageSource.getMessage("app.name", null, locale);
String smsContent = messageSource.getMessage("sms.password.recovery", new Object[] { otp }, locale);
Message message = new TextMessage(appName, phone, smsContent);
try {
smsClient.submitMessage(message);
} catch (NexmoClientException | IOException e) {
log.error("Error while sending recovery password message to phone number [{}]", phone, e);
throw new UserActivationException("Error while recovering password for user with phone: " + phone, e);
}
}
and this test:
#Before
public void setup() {
LocaleContextHolder.resetLocaleContext();
Mockito.when(tokenGenerator.generateOtp(Mockito.any())).thenReturn(14);
}
#Test(timeout = 3000)
public void testSendPasswordRecoverySmsAsyncError() throws Exception {
// Mocks.
LocaleContextHolder.setLocale(Locale.ENGLISH, true);
String mockPhone = "333";
User mockUser = mockModelBuilder.user(true, true);
Mockito.when(userDao.findByPhone(mockPhone, User.class)).then(r -> {
// TODO
return Optional.of(mockUser);
});
CountDownLatch latch = new CountDownLatch(1);
ArgumentCaptor<TextMessage> messageCaptor = ArgumentCaptor.forClass(TextMessage.class);
Mockito.when(smsClient.submitMessage(messageCaptor.capture())).then(r -> {
latch.countDown();
throw new NexmoClientException();
});
// Test.
service.sendPasswordRecoverySmsAsync(mockPhone);
latch.await();
// Assertions.
Assert.assertTrue(true);
TextMessage actualMessage = messageCaptor.getValue();
Assert.assertEquals("myApp", actualMessage.getFrom());
Assert.assertEquals(mockPhone, actualMessage.getTo());
Assert.assertEquals("Your password recovery code for myApp app is 14", actualMessage.getMessageBody());
}
I would expect the ouput of my test being "en" and this works properly if I launch only this one. However, when I run all my tests, the ouput is "it". This is probably either because in other tests I set an ITALIAN locale or because it's getting the system default.
But why is it getting the wrong one even when I'm resetting it explicitly?
For solely the purpose of testing such localization cases, you might just need to add the following on your test method. This will essentially mark the context Dirty and recreate it, depending on whether you mention as the context being Dirty either before or After the test execution.
#DirtiesContext(methodMode = MethodMode.AFTER_METHOD)
#Test(timeout = 3000)
public void testSendPasswordRecoverySmsAsyncError() throws Exception {...
Please refer the documentation here
Hope this helps, as it resolved a similar problem for me.
Play 2.5 Highlights states
Better control over WebSocket frames
The Play 2.5 WebSocket API gives you direct control over WebSocket frames. You can now send and receive binary, text, ping, pong and close frames. If you don’t want to worry about this level of detail, Play will still automatically convert your JSON or XML data into the right kind of frame.
However
https://www.playframework.com/documentation/2.5.x/JavaWebSockets has examples around LegacyWebSocket which is deprecated
What is the recommended API/pattern for Java WebSockets? Is using
LegacyWebSocket the only option for java websockets?
Are there any examples using new Message types ping/pong to implement a heartbeat?
The official documentation on this is disappointingly very sparse. Perhaps in Play 2.6 we'll see an update to this. However, I will provide an example below on how to configure a chat websocket in Play 2.5, just to help out those in need.
Setup
AController.java
#Inject
private Materializer materializer;
private ActorRef chatSocketRouter;
#Inject
public AController(#Named("chatSocketRouter") ActorRef chatInjectedActor) {
this.chatSocketRouter = chatInjectedActor;
}
// Make a chat websocket for a user
public WebSocket chatSocket() {
return WebSocket.Json.acceptOrResult(request -> {
String authToken = getAuthToken();
// Checking of token
if (authToken == null) {
return forbiddenResult("No [authToken] supplied.");
}
// Could we find the token in the database?
final AuthToken token = AuthToken.findByToken(authToken);
if (token == null) {
return forbiddenResult("Could not find [authToken] in DB. Login again.");
}
User user = token.getUser();
if (user == null) {
return forbiddenResult("You are not logged in to view this stream.");
}
Long userId = user.getId();
// Create a function to be run when we initialise a flow.
// A flow basically links actors together.
AbstractFunction1<ActorRef, Props> getWebSocketActor = new AbstractFunction1<ActorRef, Props>() {
#Override
public Props apply(ActorRef connectionProperties) {
// We use the ActorRef provided in the param above to make some properties.
// An ActorRef is a fancy word for thread reference.
// The WebSocketActor manages the web socket connection for one user.
// WebSocketActor.props() means "make one thread (from the WebSocketActor) and return the properties on how to reference it".
// The resulting Props basically state how to construct that thread.
Props properties = ChatSocketActor.props(connectionProperties, chatSocketRouter, userId);
// We can have many connections per user. So we need many ActorRefs (threads) per user. As you can see from the code below, we do exactly that. We have an object called
// chatSocketRouter which holds a Map of userIds -> connectionsThreads and we "tell"
// it a lightweight object (UserMessage) that is made up of this connecting user's ID and the connection.
// As stated above, Props are basically a way of describing an Actor, or dumbed-down, a thread.
// In this line, we are using the Props above to
// reference the ActorRef we've just created above
ActorRef anotherUserDevice = actorSystem.actorOf(properties);
// Create a lightweight object...
UserMessage routeThisUser = new UserMessage(userId, anotherUserDevice);
// ... to tell the thread that has our Map that we have a new connection
// from a user.
chatSocketRouter.tell(routeThisUser, ActorRef.noSender());
// We return the properties to the thread that will be managing this user's connection
return properties;
}
};
final Flow<JsonNode, JsonNode, ?> jsonNodeFlow =
ActorFlow.<JsonNode, JsonNode>actorRef(getWebSocketActor,
100,
OverflowStrategy.dropTail(),
actorSystem,
materializer).asJava();
final F.Either<Result, Flow<JsonNode, JsonNode, ?>> right = F.Either.Right(jsonNodeFlow);
return CompletableFuture.completedFuture(right);
});
}
// Return this whenever we want to reject a
// user from connecting to a websocket
private CompletionStage<F.Either<Result, Flow<JsonNode, JsonNode, ?>>> forbiddenResult(String msg) {
final Result forbidden = Results.forbidden(msg);
final F.Either<Result, Flow<JsonNode, JsonNode, ?>> left = F.Either.Left(forbidden);
return CompletableFuture.completedFuture(left);
}
ChatSocketActor.java
public class ChatSocketActor extends UntypedActor {
private final ActorRef out;
private final Long userId;
private ActorRef chatSocketRouter;
public ChatSocketActor(ActorRef out, ActorRef chatSocketRouter, Long userId) {
this.out = out;
this.userId = userId;
this.chatSocketRouter = chatSocketRouter;
}
public static Props props(ActorRef out, ActorRef chatSocketRouter, Long userId) {
return Props.create(ChatSocketActor.class, out, chatSocketRouter, userId);
}
// Add methods here handling each chat connection...
}
ChatSocketRouter.java
public class ChatSocketRouter extends UntypedActor {
public ChatSocketRouter() {}
// Stores userIds to websockets
private final HashMap<Long, List<ActorRef>> senders = new HashMap<>();
private void addSender(Long userId, ActorRef actorRef){
if (senders.containsKey(userId)) {
final List<ActorRef> actors = senders.get(userId);
actors.add(actorRef);
senders.replace(userId, actors);
} else {
List<ActorRef> l = new ArrayList<>();
l.add(actorRef);
senders.put(userId, l);
}
}
private void removeSender(ActorRef actorRef){
for (List<ActorRef> refs : senders.values()) {
refs.remove(actorRef);
}
}
#Override
public void onReceive(Object message) throws Exception {
ActorRef sender = getSender();
// Handle messages sent to this 'router' here
if (message instanceof UserMessage) {
UserMessage userMessage = (UserMessage) message;
addSender(userMessage.userId, userMessage.actorRef);
// Watch sender so we can detect when they die.
getContext().watch(sender);
} else if (message instanceof Terminated) {
// One of our watched senders has died.
removeSender(sender);
} else {
unhandled(message);
}
}
}
Example
Now whenever you want to send a client with a websocket connection a message you can do something like:
ChatSenderController.java
private ActorRef chatSocketRouter;
#Inject
public ChatSenderController(#Named("chatSocketRouter") ActorRef chatInjectedActor) {
this.chatSocketRouter = chatInjectedActor;
}
public static void sendMessage(Long sendToId) {
// E.g. send the chat router a message that says hi
chatSocketRouter.tell(new Message(sendToId, "Hi"));
}
ChatSocketRouter.java
#Override
public void onReceive(Object message) throws Exception {
// ...
if (message instanceof Message) {
Message messageToSend = (Message) message;
// Loop through the list above and send the message to
// each connection. For example...
for (ActorRef wsConnection : senders.get(messageToSend.getSendToId())) {
// Send "Hi" to each of the other client's
// connected sessions
wsConnection.tell(messageToSend.getMessage());
}
}
// ...
}
Again, I wrote the above to help out those in need. After scouring the web I could not find a reasonable and simple example. There is an open issue for this exact topic. There are also some examples online but none of them were easy to follow. Akka has some great documentation but mixing it in with Play was a tough mental task.
Please help improve this answer if you see anything that is amiss.
I had written a code to fetch twitter tweets using kafka, Its working fine but it is not working for partitions. I want to create 3 partitions for one topic .. how to pass the values to partitioner class.. Any suggestions where i am doing wrong
public class kafkaSpoutFetchingRealTweets {
private String consumerKey;
private String consumerSecret;
private String accessToken;
private String accessTokenSecret;
private TwitterStream twitterStream;
/**
* #param contxt
*/
void start(final Context context) {
/** Producer properties **/
Properties props = new Properties();
props.put("metadata.broker.list",
context.getString(Constant.BROKER_LIST));
props.put("partitioner.class","SimplePartitioner");
props.put("serializer.class", context.getString(Constant.SERIALIZER));
props.put("request.required.acks",
context.getString(Constant.REQUIRED_ACKS));
props.put("producer.type", "async");
// props.put("partitioner.class", context.getClass());
ProducerConfig config = new ProducerConfig(props);
final Producer<String, String> producer = new Producer<String, String>(
config);
/** Twitter properties **/
consumerKey = context.getString(Constant.CONSUMER_KEY_KEY);
consumerSecret = context.getString(Constant.CONSUMER_SECRET_KEY);
accessToken = context.getString(Constant.ACCESS_TOKEN_KEY);
accessTokenSecret = context.getString(Constant.ACCESS_TOKEN_SECRET_KEY);
ConfigurationBuilder cb = new ConfigurationBuilder();
cb.setOAuthConsumerKey(consumerKey);
cb.setOAuthConsumerSecret(consumerSecret);
cb.setOAuthAccessToken(accessToken);
cb.setOAuthAccessTokenSecret(accessTokenSecret);
cb.setJSONStoreEnabled(true);
cb.setIncludeEntitiesEnabled(true);
twitterStream = new TwitterStreamFactory(cb.build()).getInstance();
/** Twitter listener **/
StatusListener listener = new StatusListener() {
// The onStatus method is executed every time a new tweet comes
// in.
public void onStatus(Status status) {
if(("en".equals(status.getLang())) && ("en".equals(status.getUser().getLang()))){
KeyedMessage<String, String> data = new KeyedMessage<String, String>(
context.getString(Constant.data),
DataObjectFactory.getRawJSON(status));
producer.send(data);
System.out.println(DataObjectFactory.getRawJSON(status));
}
}
}
public void onDeletionNotice(
StatusDeletionNotice statusDeletionNotice) {
}
public void onTrackLimitationNotice(int numberOfLimitedStatuses) {
}
public void onScrubGeo(long userId, long upToStatusId) {
}
public void onException(Exception ex) {
ex.printStackTrace();
logger.info("Shutting down Twitter sample stream...");
twitterStream.shutdown();
}
public void onStallWarning(StallWarning warning) {
System.out.println("stallWarning");
}
};
String[] lang = { "en" };
fq.language(lang);
twitterStream.addListener(listener);
twitterStream.sample();
}
public static void main(String[] args) {
try {
Context context = new Context(args[0]);
kafkaSpoutFetchingRealTweets tp = new kafkaSpoutFetchingRealTweets();
tp.start(context);
} catch (Exception e) {
e.printStackTrace();
logger.info(e.getMessage());
}
}
}
So there are a couple problems.
Your question and code don't match up. Your questions is asking about creating a topic with 3 partitions. But the code and example that you provided explains how to determine which partition the message should be sent to given that you've already created a topic with 3 partitions.
If you're actually wanting to create a topic with 3 partitions, you need to use the command line client. A sample can be found here, http://kafka.apache.org/documentation.html#quickstart
If you're actually wanting to just determine what partition you need to send data too. You'll need to provide more information about the actual problem you're encountering? Are they all going to the same partition? Then you need to look at how you're calculating the partition in your SimplePartitioner class that you're specifying in your config. What is in the SimplePartitioner class?
props.put("partitioner.class","SimplePartitioner");