Skip to contentSkip to navigationSkip to topbar
Rate this page:
On this page

Masked Phone Numbers with Java and Servlets


This Java Servlets sample application is modeled after the amazing rental experience created by AirBnB(link takes you to an external page), but with more Klingons(link takes you to an external page).

Host users can offer rental properties which other guest users can reserve. The guest and the host can then anonymously communicate via a disposable Twilio phone number created just for a reservation. In this tutorial, we'll show you the key bits of code to make this work.

To run this sample app yourself, download the code and follow the instructions on GitHub(link takes you to an external page).

(warning)

Warning

Read how Lyft uses masked phone numbers to let customers securely contact drivers.(link takes you to an external page)


Create a Reservation

create-a-reservation page anchor

The first step in connecting a guest and host is creating a reservation. Here, we handle a form submission for a new reservation which contains the message. The guest's information is pulled out from the logged user.

src/main/java/org/twilio/airtng/servlets/ReservationServlet.java


_81
package org.twilio.airtng.servlets;
_81
_81
import org.twilio.airtng.lib.notifications.SmsNotifier;
_81
import org.twilio.airtng.lib.servlets.WebAppServlet;
_81
import org.twilio.airtng.lib.web.request.validators.RequestParametersValidator;
_81
import org.twilio.airtng.models.Reservation;
_81
import org.twilio.airtng.models.User;
_81
import org.twilio.airtng.models.VacationProperty;
_81
import org.twilio.airtng.repositories.ReservationRepository;
_81
import org.twilio.airtng.repositories.UserRepository;
_81
import org.twilio.airtng.repositories.VacationPropertiesRepository;
_81
_81
import javax.servlet.ServletException;
_81
import javax.servlet.http.HttpServletRequest;
_81
import javax.servlet.http.HttpServletResponse;
_81
import java.io.IOException;
_81
_81
public class ReservationServlet extends WebAppServlet {
_81
_81
private final VacationPropertiesRepository vacationPropertiesRepository;
_81
private final ReservationRepository reservationRepository;
_81
private UserRepository userRepository;
_81
private SmsNotifier smsNotifier;
_81
_81
public ReservationServlet() {
_81
this(new VacationPropertiesRepository(), new ReservationRepository(), new UserRepository(), new SmsNotifier());
_81
}
_81
_81
public ReservationServlet(VacationPropertiesRepository vacationPropertiesRepository, ReservationRepository reservationRepository, UserRepository userRepository, SmsNotifier smsNotifier) {
_81
super();
_81
this.vacationPropertiesRepository = vacationPropertiesRepository;
_81
this.reservationRepository = reservationRepository;
_81
this.userRepository = userRepository;
_81
this.smsNotifier = smsNotifier;
_81
}
_81
_81
@Override
_81
public void doGet(HttpServletRequest request, HttpServletResponse response)
_81
throws ServletException, IOException {
_81
_81
VacationProperty vacationProperty = vacationPropertiesRepository.find(Long.parseLong(request.getParameter("id")));
_81
request.setAttribute("vacationProperty", vacationProperty);
_81
request.getRequestDispatcher("/reservation.jsp").forward(request, response);
_81
}
_81
_81
@Override
_81
public void doPost(HttpServletRequest request, HttpServletResponse response)
_81
throws ServletException, IOException {
_81
_81
super.doPost(request, response);
_81
_81
String message = null;
_81
VacationProperty vacationProperty = null;
_81
_81
if (isValidRequest()) {
_81
message = request.getParameter("message");
_81
String propertyId = request.getParameter("propertyId");
_81
vacationProperty = vacationPropertiesRepository.find(Long.parseLong(propertyId));
_81
_81
User currentUser = userRepository.find(sessionManager.get().getLoggedUserId(request));
_81
Reservation reservation = reservationRepository.create(new Reservation(message, vacationProperty, currentUser));
_81
smsNotifier.notifyHost(reservation);
_81
response.sendRedirect("/properties");
_81
}
_81
preserveStatusRequest(request, message, vacationProperty);
_81
request.getRequestDispatcher("/reservation.jsp").forward(request, response);
_81
}
_81
_81
@Override
_81
protected boolean isValidRequest(RequestParametersValidator validator) {
_81
_81
return validator.validatePresence("message");
_81
}
_81
_81
private void preserveStatusRequest(
_81
HttpServletRequest request,
_81
String message, Object vacationProperty) {
_81
request.setAttribute("message", message);
_81
request.setAttribute("vacationProperty", vacationProperty);
_81
}
_81
}

Part of our reservation system is receiving reservation requests from potential renters. However, these reservations need to be confirmed. Let's see how we would handle this step.


Before the reservation is finalized, the host needs to confirm that the property was reserved. Learn how to automate this process in our first AirTNG tutorial, Workflow Automation(link takes you to an external page).

src/main/java/org/twilio/airtng/servlets/ReservationConfirmationServlet.java


_70
package org.twilio.airtng.servlets;
_70
_70
import com.twilio.twiml.MessagingResponse;
_70
import com.twilio.twiml.TwiMLException;
_70
import org.twilio.airtng.lib.helpers.TwiMLHelper;
_70
import org.twilio.airtng.lib.notifications.SmsNotifier;
_70
import org.twilio.airtng.lib.servlets.WebAppServlet;
_70
import org.twilio.airtng.models.Reservation;
_70
import org.twilio.airtng.models.User;
_70
import org.twilio.airtng.repositories.ReservationRepository;
_70
import org.twilio.airtng.repositories.UserRepository;
_70
_70
import javax.servlet.ServletException;
_70
import javax.servlet.http.HttpServletRequest;
_70
import javax.servlet.http.HttpServletResponse;
_70
import java.io.IOException;
_70
_70
public class ReservationConfirmationServlet extends WebAppServlet {
_70
_70
private UserRepository userRepository;
_70
private ReservationRepository reservationRepository;
_70
private SmsNotifier smsNotifier;
_70
_70
public ReservationConfirmationServlet() {
_70
this(new UserRepository(), new ReservationRepository(), new SmsNotifier());
_70
}
_70
_70
public ReservationConfirmationServlet(UserRepository userRepository, ReservationRepository reservationRepository, SmsNotifier smsNotifier) {
_70
super();
_70
this.userRepository = userRepository;
_70
this.reservationRepository = reservationRepository;
_70
this.smsNotifier = smsNotifier;
_70
}
_70
_70
@Override
_70
public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
_70
_70
String phone = request.getParameter("From");
_70
String smsContent = request.getParameter("Body");
_70
_70
String smsResponseText = "Sorry, it looks like you don't have any reservations to respond to.";
_70
_70
try {
_70
User user = userRepository.findByPhoneNumber(phone);
_70
Reservation reservation = reservationRepository.findFirstPendantReservationsByUser(user.getId());
_70
if (reservation != null) {
_70
if (smsContent.contains("yes") || smsContent.contains("accept"))
_70
reservation.confirm();
_70
else
_70
reservation.reject();
_70
reservationRepository.update(reservation);
_70
_70
smsResponseText = String.format("You have successfully %s the reservation", reservation.getStatus().toString());
_70
smsNotifier.notifyGuest(reservation);
_70
}
_70
_70
respondSms(response, smsResponseText);
_70
_70
} catch (Exception e) {
_70
throw new RuntimeException(e);
_70
}
_70
}
_70
_70
private void respondSms(HttpServletResponse response, String message)
_70
throws IOException, TwiMLException {
_70
MessagingResponse twiMLResponse = TwiMLHelper.buildSmsRespond(message);
_70
response.setContentType("text/xml");
_70
response.getWriter().write(twiMLResponse.toXml());
_70
}
_70
}

Once the reservation is confirmed, we need to purchase a Twilio number that the guest and host can use to communicate.


Purchase a Twilio Number

purchase-a-twilio-number page anchor

Here we use a Twilio Java helper library(link takes you to an external page) to search for and buy a new phone number to associate with the reservation. When we buy the number, we designate a Twilio Application that will handle webhook(link takes you to an external page) requests when the new number receives an incoming call or text.

We then save the new phone number on our Reservation model, so when our app receives calls or texts to this number, we'll know which reservation the call or text belongs to.

src/main/java/org/twilio/airtng/lib/phonenumber/Purchaser.java


_57
package org.twilio.airtng.lib.phonenumber;
_57
_57
import com.twilio.base.ResourceSet;
_57
import com.twilio.http.TwilioRestClient;
_57
import com.twilio.rest.api.v2010.account.IncomingPhoneNumberCreator;
_57
import com.twilio.rest.api.v2010.account.availablephonenumbercountry.Local;
_57
import com.twilio.type.PhoneNumber;
_57
import org.twilio.airtng.lib.Config;
_57
_57
public class Purchaser {
_57
_57
private final TwilioRestClient client;
_57
_57
public Purchaser() {
_57
client = new TwilioRestClient.Builder(Config.getAccountSid(), Config.getAuthToken()).build();
_57
}
_57
_57
public Purchaser(TwilioRestClient client) {
_57
this.client = client;
_57
}
_57
_57
public String buyNumber(Integer areaCode) {
_57
ResourceSet<Local> availableNumbersForGivenArea = Local.reader("US")
_57
.setAreaCode(areaCode)
_57
.setSmsEnabled(true)
_57
.setVoiceEnabled(true)
_57
.read();
_57
_57
if (availableNumbersForGivenArea.iterator().hasNext()) {
_57
PhoneNumber availableNumber = createBuyNumber(
_57
availableNumbersForGivenArea.iterator().next().getPhoneNumber()
_57
);
_57
_57
return availableNumber.toString();
_57
} else {
_57
ResourceSet<Local> generalAvailableNumbers = Local.reader("US")
_57
.setSmsEnabled(true)
_57
.setVoiceEnabled(true)
_57
.read();
_57
if (generalAvailableNumbers.iterator().hasNext()) {
_57
PhoneNumber availableNumber = createBuyNumber(
_57
generalAvailableNumbers.iterator().next().getPhoneNumber()
_57
);
_57
return availableNumber.toString();
_57
} else {
_57
return null;
_57
}
_57
}
_57
}
_57
_57
private PhoneNumber createBuyNumber(PhoneNumber phoneNumber) {
_57
return new IncomingPhoneNumberCreator(phoneNumber)
_57
.setSmsApplicationSid(Config.getApplicationSid())
_57
.setVoiceApplicationSid(Config.getApplicationSid())
_57
.create(client).getPhoneNumber();
_57
}
_57
}

Now that each reservation has a Twilio Phone Number, we can see how the application will look up reservations as guest or host calls come in.


When someone sends an SMS or calls one of the Twilio numbers you have configured, Twilio makes a request to the URL you set in the Twiml app. In this request, Twilio includes some useful information including:

  • The incomingPhoneNumber number that originally called or sent an SMS.
  • The anonymousPhoneNumber Twilio number that triggered this request.

Take a look at Twilio's SMS Documentation and Twilio's Voice Documentation for a full list of the parameters you can use.

In our servlet we use the to parameter sent by Twilio to find a reservation that has the number we bought stored in it, as this is the number both hosts and guests will call and send SMS to.

src/main/java/org/twilio/airtng/servlets/BaseExchangeServlet.java


_43
package org.twilio.airtng.servlets;
_43
_43
import com.twilio.twiml.TwiML;
_43
import com.twilio.twiml.TwiMLException;
_43
import org.twilio.airtng.lib.servlets.WebAppServlet;
_43
import org.twilio.airtng.models.Reservation;
_43
import org.twilio.airtng.repositories.ReservationRepository;
_43
_43
import javax.servlet.http.HttpServletResponse;
_43
import java.io.IOException;
_43
import java.util.Objects;
_43
_43
public class BaseExchangeServlet extends WebAppServlet {
_43
protected ReservationRepository reservationRepository;
_43
_43
public BaseExchangeServlet(ReservationRepository reservationRepository) {
_43
this.reservationRepository = reservationRepository;
_43
}
_43
_43
protected String gatherOutgoingPhoneNumber(String incomingPhoneNumber, String anonymousPhoneNumber) {
_43
String outgoingPhoneNumber = null;
_43
_43
Reservation reservation = reservationRepository.findByAnonymousPhoneNumber(anonymousPhoneNumber);
_43
_43
if (Objects.equals(reservation.getUser().getPhoneNumber(), incomingPhoneNumber)) {
_43
outgoingPhoneNumber = reservation.getVacationProperty().getUser().getPhoneNumber();
_43
} else {
_43
outgoingPhoneNumber = reservation.getUser().getPhoneNumber();
_43
}
_43
_43
return outgoingPhoneNumber;
_43
}
_43
_43
protected void respondTwiML(HttpServletResponse response, TwiML twiMLResponse)
_43
throws IOException {
_43
response.setContentType("text/xml");
_43
try {
_43
response.getWriter().write(twiMLResponse.toXml());
_43
} catch (TwiMLException e) {
_43
e.printStackTrace();
_43
}
_43
}
_43
}

Next, let's see how to connect the guest and the host via SMS.


Our Twilio application should be configured to send HTTP requests to this controller method on any incoming text message. Our app responds with TwiML to tell Twilio what to do in response to the message.

If the initial message sent to the anonymous number was sent by the host, we forward it on to the guest. Conversely, if the original message was sent by the guest, we forward it to the host.

To find the outgoing number we'll use the gatherOutgoingPhoneNumberAsync helper method.

src/main/java/org/twilio/airtng/servlets/ExchangeSmsServlet.java


_40
package org.twilio.airtng.servlets;
_40
_40
import com.twilio.twiml.Body;
_40
import com.twilio.twiml.Message;
_40
import com.twilio.twiml.MessagingResponse;
_40
import org.twilio.airtng.repositories.ReservationRepository;
_40
_40
import javax.servlet.ServletException;
_40
import javax.servlet.http.HttpServletRequest;
_40
import javax.servlet.http.HttpServletResponse;
_40
import java.io.IOException;
_40
_40
public class ExchangeSmsServlet extends BaseExchangeServlet {
_40
_40
@SuppressWarnings("unused")
_40
public ExchangeSmsServlet() {
_40
this(new ReservationRepository());
_40
}
_40
_40
public ExchangeSmsServlet(ReservationRepository reservationRepository) {
_40
super(reservationRepository);
_40
}
_40
_40
@Override
_40
public void doPost(HttpServletRequest request, HttpServletResponse response)
_40
throws ServletException, IOException {
_40
_40
String from = request.getParameter("From");
_40
String to = request.getParameter("To");
_40
String body = request.getParameter("Body");
_40
_40
String outgoingNumber = gatherOutgoingPhoneNumber(from, to);
_40
_40
MessagingResponse messagingResponse = new MessagingResponse.Builder()
_40
.message(new Message.Builder().body(new Body(body)).to(outgoingNumber).build())
_40
.build();
_40
_40
respondTwiML(response, messagingResponse);
_40
}
_40
}

Let's see how to connect the guest and the host via phone call next.


Our Twilio application will send HTTP requests to this method on any incoming voice call. Our app responds with TwiML instructions that tell Twilio to Play an introductory MP3 audio file and then Dial either the guest or host, depending on who initiated the call.

src/main/java/org/twilio/airtng/servlets/ExchangeVoiceServlet.java


_42
package org.twilio.airtng.servlets;
_42
_42
import com.twilio.twiml.Dial;
_42
import com.twilio.twiml.Number;
_42
import com.twilio.twiml.Play;
_42
import com.twilio.twiml.VoiceResponse;
_42
import org.twilio.airtng.repositories.ReservationRepository;
_42
_42
import javax.servlet.ServletException;
_42
import javax.servlet.http.HttpServletRequest;
_42
import javax.servlet.http.HttpServletResponse;
_42
import java.io.IOException;
_42
_42
public class ExchangeVoiceServlet extends BaseExchangeServlet {
_42
_42
@SuppressWarnings("unused")
_42
public ExchangeVoiceServlet() {
_42
this(new ReservationRepository());
_42
}
_42
_42
public ExchangeVoiceServlet(ReservationRepository reservationRepository) {
_42
super(reservationRepository);
_42
}
_42
_42
@Override
_42
public void doPost(HttpServletRequest request, HttpServletResponse response)
_42
throws ServletException, IOException {
_42
_42
String from = request.getParameter("From");
_42
String to = request.getParameter("To");
_42
_42
String outgoingNumber = gatherOutgoingPhoneNumber(from, to);
_42
_42
VoiceResponse voiceResponse = new VoiceResponse.Builder()
_42
.play(new Play.Builder("http://howtodocs.s3.amazonaws.com/howdy-tng.mp3").build())
_42
.dial(new Dial.Builder().number(new Number.Builder(outgoingNumber).build()).build())
_42
.build();
_42
_42
respondTwiML(response, voiceResponse);
_42
}
_42
_42
}

That's it! We've just implemented anonymous communications that allow your customers to connect while protecting their privacy.


If you're a Java developer working with Twilio, you might want to check out these other tutorials:

IVR: Phone Tree

Create a seamless customer service experience by building an IVR Phone Tree for your company.

Click To Call

Allow your company to convert web traffic into phone calls with the click of a button.

Did this help?

did-this-help page anchor

Thanks for checking out this tutorial! If you have any feedback to share with us, we'd love to hear it. Tweet @twilio(link takes you to an external page) to let us know what you think.


Rate this page: